2016-07-26 11 views
2

Ich lerne über FunctionalInterface, die in Java 8 vorhanden ist. Nachdem ich einige grundlegende funktionale Beispiele gemacht habe, habe ich versucht, dasselbe mit GenericType-Parametern zu tun.Java 8 Generic java.util.Function

public class Main { 

    public enum LocType { 
     Area, Country 
    } 

    public <T> Function<T, T> getCreateFunction(LocType type) { 

     AreaService areaService = new AreaService(); 
     CountryService countryService = new CountryService(); 
     switch(type) { 
      case Area : return areaService::createArea; 
      case Country : return countryService::createCountry; 
      default : return null; 
     } 
    } 

} 

public class AreaService { 

    public Area createArea(Area area) { 
     // some logic 
     return area; 
    } 

} 

public class CountryService { 

    public Country createCountry(Country country) { 
     // some logic 
     return country; 
    } 

} 

// Area & Country are Model Classes 

Aber Eclipse Compiler wirft Fehler als

The type AreaService does not define createArea(T) that is applicable here

Ist es nicht möglich, generische Typparameter in FunctionalInterface zu definieren ..?

+7

Sie können keine generische Funktion haben, deren Rückgabetyp von einem Laufzeitparameter abhängt: Rückgabetypen werden zur Kompilierzeit bestimmt; Der Wert des Parameters ist nur zur Laufzeit bekannt. –

+0

Auch sieht mir sehr verdächtig. Das heißt, dieses Generikum beruht auf zwei Arten, aber sie müssen gleich sein. Es sei denn, ich verpasse etwas? EDIT: Warte, ich sehe jetzt, vergiss es. –

+1

Es ist nicht klar, was die Rückgabetypen 'createArea()' und 'createCountry()' sind. Sie können auch 'UnaryOperator ' anstelle von 'Funktion ' betrachten (vorausgesetzt, dass das hier korrekt ist). – shmosel

Antwort

3

Sie können die Class Klasse als Typ Token hier verwenden. (Ich werde nur einige offensichtliche Probleme mit dem von Ihnen geposteten Code ignorieren, wie z. B. nicht in der Lage sein, new AreaService() usw. anzurufen). Statt eine ENUM für die Art der Sache definieren wollen Sie Ihre Funktion (manipulieren?) Erstellen, verwenden Sie Class als Parameter:

@SuppressWarnings("unchecked") 
public <T> Function<T, T> getCreateFunction(Class<T> type) { 

    AreaService areaService = new AreaService(); 
    CountryService countryService = new CountryService(); 
    if (type == Area.class) { 
     return t -> (T) areaService.createArea((Area)t); 
    } else if (type == Country.class) { 
     return t -> (T) countryService.createCountry((Country)t); 
    } 
    return null ; // may be better to throw an IllegalArgumentException("Unsupported type") 
} 

Und jetzt können Sie Dinge tun, wie

Main main = new Main(); 
Function<Area, Area> areaChanger = main.getCreateFunction(Area.class); 
Function<Country, Country> countryChanger = main.getCreateFunction(Country.class); 

Die So funktioniert das, dass das Klassenliteral T.class für jede Klasse T vom Typ Class<T> ist. So hat Area.class Typ Class<Area>, und Country.class hat Typ Class<Country>.

Wenn Sie also getCreateFunction(Area.class) nennen, kann der Compiler ableiten, dass T ist Area in diesem Aufruf und der Rückgabetyp ist dann Function<Area, Area>. Auf der anderen Seite, in der Implementierung von getCreateFunction, während der Compiler kann nur folgern, dass der Parameter für das Lambda ist vom unbekannten Typ T, wir wissen, dass T muss von dem Typ, der durch den Parameter type; d. h. wenn type==Area.class können wir t sicher zu Area und (der von areaService.createArea zurückgegebene Wert) Area zu T. So sind die Abgüsse sicher (auch der Abguss zum unbekannten Typ T).

Wenn Sie die Werte weiter einschränken möchten, die an getCreateFunction(...) übergeben werden könnten, könnten Sie eine Marker-Schnittstelle definieren: z. public interface Region { } und machen Area und Country implementieren diese Schnittstelle.Dann definieren

public <T extends Region> Function<T,T> getCreateFunction(Class<T> type) {...} 

und es wäre nur gültig, in Klasse-Token für die Klassen übergeben, die (oder Schnittstellen, die erweitert) implementiert Region.

+0

Ich fragte mich, was ist eigentlich der Zweck von 'FunctionalInterface', wenn es nicht für generische Typparameter verwendet werden kann, bis jetzt .. !! Es scheint, dass wir generische Typparameter definieren können, aber nicht wie üblich '' '. Danke .. –

+0

Was meinst du? Ich habe Ihnen nur gezeigt, wie man es für generische Typparameter verwendet. –

+0

Editiert den Kommentar. Nur eine falsche Kommunikation. –

0

Es ist möglich, generische Typparameter zu definieren. Sie können Beispiele in jdk wie

andThen

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { 
    Objects.requireNonNull(after); 
    return (T t) -> after.apply(apply(t)); 
} 

oder identity

static <T> Function<T, T> identity() { 
    return t -> t; 
} 

finden Was in Ihrem Beispiel ist nicht funktioniert, dass areaService::createArea und countryService::createCountryFunctions für konkrete Typen sind.

Der Compiler kann Area nicht dem generischen Typ T zuordnen. Weil T kann alles sein, d. H. Integer oder Stream