Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala can't infer type arguments of Java method

Tags:

types

scala

I have the following sophisticated type hierarchy in Java:

// the first type
interface Element<Type extends Element<Type>> {
    Type foo(Type a, Type b);
}

// the second type
interface Payload<Type extends Payload<Type>> {
    Type bar(Type[] array);
}

// some toy implementation
final class SomePayload implements Payload<SomePayload> {
    @Override
    public SomePayload bar(SomePayload[] array) { return array[0]; }
}

// mix of first and second interfaces
interface ComplicatedElement<
              PayloadT extends Payload<PayloadT>,
              ObjectT extends ComplicatedElement<PayloadT, ObjectT>>
          extends Element<ObjectT> {

    PayloadT getPayload();

    ObjectT add(ObjectT a, ObjectT b);
}

// some toy implementation
final class SomeComplicatedElement 
      implements ComplicatedElement<SomePayload, SomeComplicatedElement> {
    final SomePayload data;

    public SomeComplicatedElement(SomePayload data) {
        this.data = data;
    }

    @Override
    public SomePayload getPayload(){ return data; }

    @Override
    public SomeComplicatedElement foo(SomeComplicatedElement a, SomeComplicatedElement b) {
        return b;
    }

    @Override
    public SomeComplicatedElement add(SomeComplicatedElement a, SomeComplicatedElement b) {
        return a;
    }
}

I have some static method which deals with ComplicatedElements:

public static <PayloadT extends Payload<PayloadT>,
              ObjectT extends ComplicatedElement<PayloadT, ObjectT>>
List<ObjectT> method(ObjectT input) {
    return Collections.singletonList(input);
}

Now, from Java I can call method without problems like this:

public static void main(String[] args) {
    System.out.println(method(new SomeComplicatedElement(new SomePayload())));
}

However, when I try to do the same thing in Scala:

import FooBarJava.{SomeComplicatedElement, SomePayload, method}

def main(args: Array[String]): Unit = {
  println(method(new SomeComplicatedElement(new SomePayload())))
}

I have this compilation error:

Error:(10, 21) inferred type arguments [Nothing,FooJava.SomeComplicatedElement] do not conform to method method's type parameter bounds [PayloadT <: FooJava.Payload[PayloadT],ObjectT <: FooJava.ComplicatedElement[PayloadT,ObjectT]] println(FooJava.method(new SomeComplicatedElement(new SomePayload())))

I can fix this by specifying type parameters explicitly:

println(method[SomePayload, SomeComplicatedElement](new SomeComplicatedElement(new SomePayload())))

But it is very annoying and I would like to avoid this (I guess it is possible since Java compiler just work fine with this). Is there any way to do so?

like image 320
Stanislav Poslavsky Avatar asked Oct 18 '22 06:10

Stanislav Poslavsky


1 Answers

(I guess it is possible since Java compiler just work fine with this)

Scala has richer type system than Java. For example in Java there is no type similar to Nothing in Scala (i.e. universal subtype). So it's possible that sometimes compiler in a language with richer type system will be unable to infer types while in similar situation compiler in a language with poorer type system can do this.

If specifying type parameters is too annoying why not to create some helper method?

private def methodWithPayload(data: SomePayload): java.util.List[SomeComplicatedElement] =
    method[SomePayload, SomeComplicatedElement](new SomeComplicatedElement(data))

methodWithPayload(new SomePayload)

In Java there is the only option <SomePayload, SomeComplicatedElement>method(..), in Scala there are two options method[SomePayload, SomeComplicatedElement](..) and method[Nothing, SomeComplicatedElement](..) and both options are valid.

like image 114
Dmytro Mitin Avatar answered Nov 15 '22 08:11

Dmytro Mitin