Java type erasure and multiple bounds
Asked Answered
G

5

6

I know that in Java generics when using a type parameter with multiple bounds,the compiler erases the type information to "the leftmost bound" (i.e. the first class/enum or interface that's on the list). So why is it that the following code compiles without problems?

public class Generic<T extends Object & Appendable & AutoCloseable> {

  T t;

  T method() throws Exception {
    t.close();
    char c='\u0000';
    t.append(c);
    return t;
  }

  public <T> T method2(T t) {
    return t;
  }  

}

shouldn't the type parameter T be treated as Object?? (thus disallowing me to call close() or append())??

Garold answered 26/12, 2015 at 11:56 Comment(4)
Which part you dont understand?method?Neile
@Neile a variable whose type is a type parameter should be able to use only members of the parameter's erasure type (which is said to be it's leftmost bound in the list). This is not the case in my code.Garold
The compiler does not have a problem with this. Shouldn't you better ask why the runtime is able to call t.close() despite of type erasure?Docilu
@Docilu why would that be? that type is erased to AutoCloseable and the OP expects it to be erased to Object. also see my answer (that is not really an answer yet)Ferromagnetic
R
0

You should read about Bridge Methods here

When compiling a class or interface that extends a parameterized class or implements a parameterized interface, the compiler may need to create a synthetic method, called a bridge method, as part of the type erasure process. You normally don't need to worry about bridge methods, but you might be puzzled if one appears in a stack trace.

Roi answered 26/12, 2015 at 12:5 Comment(2)
I know about them.It's not the question I asked,I am not extending a generic class or implementing a generic interface in my code.Garold
ok, so i use jd-gui to look inside the .class file, and found such code T method() throws Exception { ((AutoCloseable)this.t).close(); char c = '\000'; ((Appendable)this.t).append(c); return (T)this.t; }. Sorry for multiple editRoi
T
0

Your case contains multiply bound.

A type variable with multiple bounds is a subtype of all the types listed in the bound. If one of the bounds is a class, it must be specified first.

Therefore calling method close() contained in interface AutoCloseable and append() contained in interface Appendable is syntactically legal.

Trypanosome answered 26/12, 2015 at 12:7 Comment(2)
it is a subtype indeed (because the compiler doesn't allow passing a type argument that's not a subtype of all the types in the list), but since the type parameter erases to a type (the compiler cannot substitute multiple types in the bytecode) I should be able to call only the members of the erasure type.Garold
All type info is available for the compiler in compilation time. This is why it compiles. The type info is lost only in the byte code. An interesting question would be what if you reuse the compiled binary class as a dependency in an other compilation unit.Trypanosome
R
0

This is disassembled your code. Variable t is Object type. Instruction checkcast is generated before calling interface method. If the value of t does not implement the interface then ClassCastException will be thrown.

Compiled from "Generic.java"
public class Generic<T extends java.lang.Appendable & java.lang.AutoCloseable> {
  T t;

  public Generic();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  T method() throws java.lang.Exception;
    Code:
       0: aload_0
       1: getfield      #2                  // Field t:Ljava/lang/Object;
       4: checkcast     #3                  // class java/lang/AutoCloseable
       7: invokeinterface #4,  1            // InterfaceMethod java/lang/AutoCloseable.close:()V
      12: iconst_0
      13: istore_1
      14: aload_0
      15: getfield      #2                  // Field t:Ljava/lang/Object;
      18: checkcast     #5                  // class java/lang/Appendable
      21: iload_1
      22: invokeinterface #6,  2            // InterfaceMethod java/lang/Appendable.append:(C)Ljava/lang/Appendable;
      27: pop
      28: aload_0
      29: getfield      #2                  // Field t:Ljava/lang/Object;
      32: areturn

  public <T> T method2(T);
    Code:
       0: aload_1
       1: areturn
}
Recountal answered 26/12, 2015 at 12:27 Comment(1)
shouldn't it be java.lang.Appendable? I understand the casts, but otherwise,what's the meaning of "erasure to the leftmost bound"??Garold
F
0

This is not an answer per-se, just that this leftmost rule is not entire relevant - I am still trying to figure out the proper JLS parts for this (not as easy as I hope it to be), but you can see that the type is not an Object from here:

<T extends Object & Appendable & AutoCloseable> void whatType(T... args) {
    System.out.println(args.getClass().getComponentType().getSimpleName());
}


new DeleteMe().whatType(); // prints AutoCloseable
Ferromagnetic answered 17/5, 2018 at 9:20 Comment(4)
What you are showing, is the compiler’s decision for the actual array type at the call-site. The erased method argument type still is Object[]. Different callers may use different array types; you may add another caller whatType(new StringWriter()) and that caller will use StringWriter[]. And the rule behind this decision is an entirely different question, as the OP is right about assuming T to be erased to Object, though I don’t know why the OP thought that this has any relevance.Handlebar
@Handlebar thank you, so it's always erased to Object.. but how come <T extends AutoCloseable & Runnable> for this the used type is AutoCloseable and for <T extends AutoCloseable & Serializable> is Serializable? Where does the leftmost feature fits here?Ferromagnetic
I don’t know what strategy javac uses here and the specification doesn’t seem to back this up. But it’s really interesting that this behavior leads to incompatible array assignments (a Serializable[] gets passed to a formal parameter of type AutoCloseable[]) flying under the JVM’s radar while explicit casting to what-it-already-appears-to-be will fail. But, as said, this is an entirely now question (and seems to open a really big rabbit hole).Handlebar
@Handlebar I don't want to take the credit here... the question was posted initially here #50177813 and while there is an answer with lots of up-votes, I have a hard time understanding itFerromagnetic
Q
0

As per the java documentation, generic class are allowed to use all the methods dedfined in all the bound. Hence, it's allowed to use the methods of Object , Appendable and Autocloseable. Type erasure and the allowed methods are not dependent on each other. Type erasure has been introduced only to prevent an extra overhead during the run time .

Quiche answered 11/8 at 18:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.