1

let's consider:

public class Text extends BinaryComparable
    implements WritableComparable<BinaryComparable> {

We can see that Text is BinaryComparable.

Then, let's consider

@InterfaceAudience.Public
@InterfaceStability.Stable
public interface WritableComparable<T> extends Writable, Comparable<T> {

I have some class in Scala:

trait MyClass[A <: WritableComparable[A]] {

It is not possible to create

MyClass[Text] = new MyClass[Text]()

due to type mismatch. Why? After all, Text is BinaryComparable How to resolve it?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
tomek.xyz
  • 129
  • 7

2 Answers2

3

WritableComparable[BinaryComparable] is not the same as or a supertype of WritableComparable[Text], as WritableComparable is invariant in T (Java generics doesn't really have covariance or contravariance).

If WritingComparable had been declared as trait WritingComparable[-A], then it would compile.

Your last code snippet does not make much sense, though, as MyClass does not take 2 type parameters (as Luis Miguel Mejia Suarez mentioned). You shouldn't get a type mismatch error, it should tell you you have too many parameters. I think you meant only MyClass[Text].

Here's a question asking about invariance, contravariance, and covariance.

user
  • 7,435
  • 3
  • 14
  • 44
  • I edited it. Can you tell me, how to repair it? Is it possible? – tomek.xyz Oct 30 '20 at 23:30
  • 1
    @tomek.xyz I tried using `[_ >: A]`, but that [doesn't work](https://scastie.scala-lang.org/A2mYcLkuQ4Of1zKZihqykg). An [existential type](https://scastie.scala-lang.org/DyVv1M1NRue71ZffdqrfLg) may work, but it's hacky, and I'd really recommend rethinking your design, if you have any control over `MyClass`'s type parameters. – user Oct 30 '20 at 23:43
2

You can try to add one more type parameter

trait MyClass[B >: A, A <: WritableComparable[B]]

val mc: MyClass[BinaryComparable, Text] = new MyClass[BinaryComparable, Text] {}

On contrary to trait MyClass[A <: WritableComparable[_ >: A]] this doesn't produce illegal cyclic reference.

Alternatively you can define bounds in MyClass as

trait MyClass[B, A <: B with WritableComparable[B]]

val mc: MyClass[BinaryComparable, Text] = new MyClass[BinaryComparable, Text] {}

You can even exclude B with existential type (as @user proposed)

trait MyClass[A <: B with WritableComparable[B] forSome { type B }]

val mc: MyClass[Text] = new MyClass[Text] {}

Such existential types will be deprecated in Scala 3

http://dotty.epfl.ch/docs/reference/dropped-features/existential-types.html

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • Being honestly, I don't understand it exactly. `A <: WritableComparable[B]` guarantess that `A` has method like `compare(T o)`. `B >: A` does mean that `A extends B`. Why does it guarantee that `A` will be comparable to all `supertypes` of A? (called as `B` here) – tomek.xyz Nov 09 '20 at 09:32
  • In other words, it should work with `MyClass[B, A <: WritableComparable[B]]` as well. – tomek.xyz Nov 09 '20 at 09:37
  • Oh, I understand it now. Basically, we expressed that `A` has method `compareTo(B o)` and `B` is supertype of `A`. It does mean that we can compare: `new A().compareTo(*)` where * is arbitrary object being instance of supertype of `A`. – tomek.xyz Nov 09 '20 at 09:43
  • @tomek.xyz It's just generalization of `Text extends BinaryComparable implements WritableComparable`. `Text` is `A`, `BinaryComparable` is `B`. `Text extends... implements...` can be written as `A <: B with WritableComparable[B]`. So you can introduce either `trait MyClass[B >: A, A <: WritableComparable[B]]` or `trait MyClass[B, A <: B with WritableComparable[B]]`. – Dmytro Mitin Nov 09 '20 at 12:02