scalagenericsscala-java-interop

Scala class implementing Java interface - how to implement method taking array of generic type


I hava a Java interface:

public interface FooJava<Element> {
  void consume(Element[] elements);
  // more methods
}

and I want to implement it in Scala:

// attempt 1
class BarScala[T] extends FooJava[T] {
  override def consume(elements: Array[T]): Unit = ???
}

// attempt 2
class BarScala[T <: AnyRef] extends FooJava[T] {
  override def consume(elements: Array[T]): Unit = ???
}

In both attempts I get the same compilation error:

class BarScala needs to be abstract, since method consume in trait FooJava of type (elements: Array[T with Object])Unit is not defined
(Note that Array[Element with Object] does not match Array[T]: their type parameters differ)

I don't own the Java class. How can I define my Scala class to avoid this error?

I'm on Scala 2.12


Solution

  • Try

    class BarScala[T] extends FooJava[T] {
      override def consume(elements: Array[T with Object]): Unit = ???
    }
    

    or

    class BarScala[T] extends FooJava[T] {
      override def consume(elements: Array[T with AnyRef]): Unit = ???
    }
    

    The behavior can be reproduced even purely in Scala. Although for T <: Upper, T with Upper =:= T, we can't replace T with Upper with just T in overriding method

    trait Foo[T, F[_], Upper] {
      def consume(elements: F[T with Upper]): Unit
    }
    
    class Bar[T <: Upper, F[_], Upper] extends Foo[T, F, Upper] {
      implicitly[(T with Upper) =:= T] // compiles
      implicitly[F[T with Upper] =:= F[T]] // compiles
    
      override def consume(elements: F[T with Upper]): Unit = ??? // compiles
    
      //override def consume(elements: F[T]): Unit = ??? // doesn't compile
      // method consume overrides nothing
      // class Bar needs to be abstract. Missing implementation for member of trait Foo
    }
    

    Similarly,

    trait Foo[T, F[_], Upper] {
      type X <: T with Upper
      type Y >: T with Upper
      type Z = T with Upper
      type Z1 >: T with Upper <: T with Upper
      type Z2 >: T <: T
    }
    
    class Bar[T <: Upper, F[_], Upper] extends Foo[T, F, Upper] {
      override type X <: T   // compiles
      override type Y >: T   // compiles
      //override type Z = T  // doesn't compile: incompatible type in overriding, Equivalent type required when overriding a type alias
      override type Z1 = T   // compiles
      override type Z2 = T with Upper // compiles
    }
    

    Probably the thing is that for T <: Upper, T with Upper <: T and T <: T with Upper

    https://scala-lang.org/files/archive/spec/2.13/03-types.html#conformance

    but not T with Upper ≡ T

    https://scala-lang.org/files/archive/spec/2.13/03-types.html#equivalence

    This behavior is changed in Scala 3 https://scastie.scala-lang.org/DmytroMitin/LHhlufv8ReqoN6w2ZL0BLQ/1