Skip to content

Missing Java generics signature on method returning inner class object from type parameterized outer class #2585

@scabug

Description

@scabug

We are using Eclipse 3.5 Build id 20090920-1017 and Scala Eclipse plug-in 2.8.0.r19132-b20091019023339.

When declaring a method that returns an inner class object of a specific instantiation of a type parameterized class, the generated byte code is missing a signature describing the actual type instantiation.

Consider these classes

class Outer[T](val t: T) {
  class Inner {
    def getT : T = t
  }
}

class OuterImpl(x: X) extends Outer(x) {
  def newInner = new Inner
}

class X {
  def getI : OuterImpl#Inner = {
    val oImpl = new OuterImpl(this)
    new oImpl.Inner
  }
}

Inspecting the class file for class X, looking at the method "getI" reveals:

// Method descriptor SI-12 ()Ltest/scala/Outer$$Inner;
// Stack: 3, Locals: 2
public test.scala.Outer.Inner getI();

Calling the class from Scala works fine. Functions 'getT' and 'getI' have the expected return types and can be chained forever.

class TestFromScala {
  val x = new X
  val o = new OuterImpl(x)
  val i = o.newInner
  val i2 = i.getT.getI.getT.getI
}

However, calling from Java causes problems:

public class TestFromJava {
  void test() {
    final X x = new X();
    final OuterImpl o = new OuterImpl(x);
    
    final OuterImpl.Inner i1 = o.newInner();
    i1.getT().getI().getT().getI();  // <--- Error: "The method getI() is undefined for the type Object"
   
    final Outer<X>.Inner i2 = o.newInner();
    i2.getT().getI().getT().getI(); // <--- Error: "The method getI() is undefined for the type Object"
  }
}

Implementing the same classes in Java looks like this:

public class Outer<T> {
  private final T t;

  public Outer(T t) {
    this.t = t;
  }
  
  public class Inner {
    public T getT() {
      return t;
    }
  }
}
 
public class OuterImpl extends Outer<X> {
  public OuterImpl(X x) {
    super(x);
  }
  
  public OuterImpl.Inner newInner() {
    return this.new Inner();
  }
}

public class X {
  OuterImpl.Inner getI() {
    final OuterImpl oImpl = new OuterImpl(this);
    return oImpl.newInner();
  }
}

The signature for 'X.getI()' looks like this:

// Method descriptor SI-15 ()Ltest/java/Outer$$Inner;
// Signature: ()Ltest/java/Outer<Ltest/java/X;>.Inner;
// Stack: 3, Locals: 2
test.java.Outer.Inner getI();

Please note the "// Signature" line. And now everythings works from Java:

public class TestFromJava {
  void test() {
    final X x = new X();
    final OuterImpl o = new OuterImpl(x);
    
    final OuterImpl.Inner i1 = o.newInner();
    i1.getT().getI().getT().getI(); // <--- Works fine.
    
    final Outer<X>.Inner i2 = o.newInner();
    i2.getT().getI().getT().getI(); // <--- Works fine.
  }
}

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions