简体   繁体   中英

Ensure arguments to generic method have same type in trait method

I have a sealed trait and some case classes that extend that trait, like so:

sealed trait Foo
case class Bar extends Foo
case class Baz extends Foo

In a different part of my code, I have a trait with a method on it that operates on Foo s

trait Example {
    def method(arg1: Foo, arg2: Foo)
}

However, I would really like to ensure that arg1 and arg2 always have the same type; that is, they should both be either Bar or Baz , and never a mix. My first intuition is to use generics:

trait Example {
    def method[T: Foo](arg1: T, arg2: T)
}

But I run into two problems:

  1. T needs to be present on Example as far as I can tell. Can I make method generic without "infecting" the rest of the trait?
  2. I'm not actually sure my type restriction gets the result I would like. Can anyone confirm if my intuition is correct?

Actually if you want

to ensure that arg1 and arg2 always have the same type; that is, they should both be either Bar or Baz , and never a mix

then

trait Example {
    def method[T <: Foo](arg1: T, arg2: T) = ???
}

is incorrect. new Example {}.method(Bar(), Baz()) compiles because T is inferred to be Foo .

Correct is

trait Example {
  def method[T <: Foo, U <: Foo](arg1: T, arg2: U)(implicit ev: T =:= U) = ???
}

Then new Example {}.method(Bar(), Baz()) doesn't compile but new Example {}.method(Bar(), Bar()) and new Example {}.method(Baz(), Baz()) compile.

More details when generalized type constraints ( <:< , =:= and even <:!< , =:!= ) should be preferred over type bounds ( <: , >: ) can be found here:

https://blog.bruchez.name/2015/11/generalized-type-constraints-in-scala.html (See example

def tupleIfSubtype[T <: U, U](t: T, u: U) = (t, u)

vs.

def tupleIfSubtype[T, U](t: T, u: U)(implicit ev: T <:< U) = (t, u)

there.)

https://apiumhub.com/tech-blog-barcelona/scala-generics-generalized-type-constraints/ ( https://dzone.com/articles/scala-generics-generalized-type-constraints-part-3 )

https://herringtondarkholme.github.io/2014/09/30/scala-operator/

You need to specify <:

: stands for context bound and works in combination with a type class like trait Zoo[T]

<: stands for upper type bound

trait Example {
    def method[T <: Foo](arg1: T, arg2: T)
}

Update:

As @Dmytro Mitin correctly pointed out, the correct solution requires evidence check =:= to be performed.

def method[T <: Foo, U <: Foo](arg1: T, arg2: U)(implicit ev: T =:= U)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM