I am learning about FunctionalInterface
which is present in Java 8. After doing some basic Functional examples, I tried to do the same with GenericType parameters.
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
But eclipse compiler throws error as
The type AreaService does not define createArea(T) that is applicable here
Isn't it possible to define Generic Type Parameters in FunctionalInterface..?
You can use the Class
class as a type token here. (I'll just ignore some obvious issues with the code you posted, such as not being able to call new AreaService()
, etc). Instead of defining an enum for the type of thing you want your function to create (manipulate?), use Class
as the 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")
}
And now you can do things such as
Main main = new Main();
Function<Area, Area> areaChanger = main.getCreateFunction(Area.class);
Function<Country, Country> countryChanger = main.getCreateFunction(Country.class);
The way this works is that the class literal T.class
for any class T
is of type Class<T>
. So Area.class
has type Class<Area>
, and Country.class
has type Class<Country>
.
Thus if you call getCreateFunction(Area.class)
, the compiler can infer that T
is Area
in that invocation, and the return type is then Function<Area, Area>
. On the other hand, in the implementation of getCreateFunction
, while the compiler can only infer that the parameter to the lambda is of the unknown type T
, we know it T
must be of the type represented by the parameter type
; i.e. if type==Area.class
we can safely cast t
to Area
and (the value returned from areaService.createArea
) Area
to T
. Thus the casts are safe (even the cast to the unknown type T
).
If you wanted to further restrict the values that could be passed to getCreateFunction(...)
you could define a marker interface: e.g. public interface Region { }
and make Area
and Country
implement that interface. Then define
public <T extends Region> Function<T,T> getCreateFunction(Class<T> type) {...}
and it would only be valid to pass class tokens for classes that implemented (or interfaces that extended) Region
.