Java >> Java Program >  >> Java

En mindre känd Java 8-funktion:Generaliserad Target-Type Inference

När jag gick igenom listan över Java 8-funktioner, slog Generalized Target-Type Inference mig som en särskilt intressant, mindre känd pärla. Det ser ut som om Java-språkdesignerna kommer att lindra en del av smärtan som vi har haft med generika tidigare (Java 5-7). Låt oss ta en titt på deras exempel:

class List<E> {
  static <Z> List<Z> nil() {..}
  static <Z> List<Z> cons(Z head, List<Z> tail) {..}
  E head() {..}
}

Med tanke på ovanstående exempel hävdar JEP 101-funktionen att det skulle vara trevligt att kunna skriva:

// This:
List.cons(42, List.nil());
String s = List.nil().head();

// ... instead of this:
List.cons(42, List.<Integer>nil());
String s = List.<String>nil().head();

Eftersom jag själv är en flytande API-designer, blev jag glad över att se att en sådan förbättring finns på färdplanen, särskilt den senare. Vad är så spännande med dessa förändringar? Låt mig kommentera det mer i detalj:

// In addition to inferring generic types from
// assignments
List<String> l = List.nil();

// ... it would be nice for the compiler to be able
// to infer types from method argument types
List.cons(42, List.nil());

// ... or from "subsequent" method calls
String s = List.nil().head();

Så i det sista exemplet där metoder är kedjade, skulle typinferensen fördröjas tills hela tilldelningsuttrycket har utvärderats. Från den vänstra sidan av uppgiften kunde kompilatorn dra slutsatsen att <Z> binder till Stringhead() ringa upp. Denna information kan sedan användas igen för att sluta sig till den <Z> binder igen till Stringnil() samtal. Låter som en hel del knep för mig, som nil() samtalets AST-utvärderingar skulle behöva försenas tills en "beroende" sub-AST utvärderas. Är det en bra idé?

Ja, det här är så fantastiskt!

… du kanske tror. Eftersom ett flytande API som jOOQ eller Streams API skulle kunna utformas i en mycket mycket mer flytande stil, vilket fördröjer typinferensen till slutet av samtalskedjan. Så jag laddade ner den senaste utvärderingsdistributionen av JDK 8 för att testa detta med följande program :

public class InferenceTest {
    public static void main(String[] args) {
        List<String> ls = List.nil();
        List.cons(42, List.nil());
        String s = List.nil().head();
    }
}

Jag kompilerade detta och jag fick:
C:\Users\Lukas\java8>javac InferenceTest.java
InferenceTest.java:5: error: incompatible types: 
    Object cannot be converted to String
        String s = List.nil().head();
                                  ^
1 error
Så, typinferensen baserad på metodargumenttypen implementeras (och alltså kompileras), men inte typinferensen för kedjade metodanrop. Jag sökte på internet efter en förklaring och hittade denna Stack Overflow-fråga som länkade till den här intressanta tråden på lambda-dev-sändlistan. Det verkar som om Java-systemet har blivit ganska komplicerat. För komplicerat för att implementera sådana här galna slutsatser. Men ändå, en liten förbättring som kommer att uppskattas mycket när du skriver Java 8-kod varje dag. Och kanske, i Java 9, får vi val och var , som alla andra;-)
Java-tagg