Java-RFC binary compatibility on the proposed solution for the covariant return type using invokevirtual semantics

I am trying to develop an API. As part of this evolution, I need to change the return type of the method to a subclass (specialization) so that advanced clients can access new functions. Example (ignore the ugly:

public interface Entity {
  boolean a();
}

public interface Intf1 {
  Entity entity();
}

public interface Main {
  Intf1 intf();
}

Now I want to have ExtendedEntity, Intf2, and Main as follows:

public interface ExtendedEntity extends Entity {
  boolean b();
}

public interface Intf2 extends Intf1 {
  ExtendedEntity entity();
}

public interface Main {
  Intf2 intf();
}

However, since the return type of the method is part of its signature, the clients are already compiled with the previous version of the show linkage errors code (the method was not found by iirc).

Main . ( - , ) ( ). . , JVM, Java.

, , , ( ), Java .

public interface Main_Backward_Compatible {
  Intf1 intf();
}

public interface Main extends Main_Backward_Compatible{
  Intf2 intf();
}

, invokevirtual lookup ( ), , , , Intf2.

. , ( , ), . ? ( invokevirtual)?

, , - "" ? , , , .

,
.

- " " ( ):

  • Clirr 0.6.
  • IntelliJ "APIComparator".

Edit2 - , (think services). , , , ( Main) .

+3
3

, , , .

+1

, , , , . Intf1, , :

public interface Intf1<T extends Entity> {
  T entity(); //erasure is still Entity so binary compatibility
}

public interface Intf2 extends Intf1<ExtendedEntity> { //if even needed
}

public interface Main {
  Intf1<ExtendedEntity> intf(); //erasure is still Intf1, the raw type
}

# 1: . . 6 10.

# 2:

Main:

public interface Main<T, I extends Intf1<T>> {
    I intf(); //still has the same erasure as it used to, so binary compatible
}

, , , Main:

Main<ExtendedEntity, Intf2> myMain = Factory.getMeAMain();
Intf2 intf = myMain.intf();
+1

It would be easier not to change existing interfaces at all. Anyone using your new interface will still be writing new code.

Implementations of an existing Main.intf () signature may return an Intf2 instance.

Optionally, you can provide a new accessor that does not require casting:

public interface Main2 extends Main {
  Intf2 intf2();
}
0
source

Source: https://habr.com/ru/post/1760025/


All Articles