i have an enum implementing a supplier, e.g.:
public enum QUERY_FIELD implements Supplier<String> {
PRODUCT_TYPE("ProductType"),
MIN_NUMBER_OF_PARTS("MinNumberOfParts"),
MAX_NUMBER_OF_PARTS("MaxNumberOfParts");
private final String id;
QUERY_FIELD(final String id) {
this.id = id;
}
#Override
public final String get() {
return id;
}
}
i have different utility methods which can find the enum i search for depending on the query
public static <T extends Enum<T> & Supplier<String>> Optional<T> findById(final Class<T> enumClass, final String id) {
return Arrays.stream(enumClass.getEnumConstants()).filter(p -> id.equalsIgnoreCase(p.get())).findFirst();
}
public static <T extends Enum<T> & Supplier<? extends Number>> Optional<T> findById(final Class<T> enumClass, final Number id) {
return Arrays.stream(enumClass.getEnumConstants()).filter(p -> id.equals(p.get())).findFirst();
}
now i want to adapt this idea to create a utility method that just returns the list of all values depending on the suppliers type.
i tried it with:
public static <T extends Enum<T> & Supplier<? extends String>> List<String> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants()).map(Supplier::get).collect(Collectors.toList());
}
or
public static <U, T extends Enum<T> & Supplier<? extends U>> List<U> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants()).map(Supplier::get).collect(Collectors.toList());
}
which both compile but do not work,
how should i build this method?
I guess that the problem you are experiencing in the runtime issue
described in Enum, interfaces and (Java 8) lambdas: code compiles but fails at runtime; is this expected? and is cause by a known bug in Java-8 https://bugs.openjdk.java.net/browse/JDK-8141508.
The problem is with the intersection bound Enum<T> & Supplier<? extends String>. It seems that the compiler generates code where the second element in the intersection is lost and so in run-time it is as if you tried to use for the lambda an object of a class that is not guaranteed to implement Supplier but just Enum.
Since getEnumConstant is available for all Class<X> regardless wether X is an enum or not you could simply discard that part from the param-type bound and leave it as Supplier<String>:
import java.util.function.*;
import java.util.*;
import java.util.stream.*;
enum Test implements Supplier<String> {
A, B, C;
public String get() { return name(); }
}
class Main {
public static <T extends Supplier<String>> List<String> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.map(Supplier::get)
.collect(Collectors.toList());
}
public static final void main(String[] args) {
System.out.println(getValueList(Test.class).stream()
.collect(Collectors.joining(",")));
}
}
However the downside of this is that the compiler won't fail if someone provide a class that is not an enum.... in order to prevent that you can keep the intersection bound and add an extra map that explicitly cast the enum constants into suppliers:
public static <T extends Enum<T> & Supplier<String>> List<String> getValueList(final Class<T> enumClass) {
return Arrays.stream(enumClass.getEnumConstants())
.map(x -> (Supplier<String>) x) // not needed to compile but needed for run-time.
.map(Supplier::get)
.collect(Collectors.toList());
}
UPDATE
There is a even better solution for the second alternative. You can do the cast just one outside the stream, that should save you compute:
public static <T extends Enum<T> & Supplier<String>> List<String> getValueList(final Class<T> enumClass) {
final Class<Supplier<String>> asSupplierClass = enumClass;
return Arrays.stream(asSupplierClass.getEnumConstants())
.map(Supplier::get)
.collect(Collectors.toList());
}
I have not tested it, let me know whether it works.
Related
class MyObject{}
class Human extends MyObject{}
public ConcurrentSkipListMap<String, <T extends MyObject>> get(Class<T> clazz, Object... vars){
//...
}
//use the get() with
ConcurrentSkipListMap<String, Human> mapOfAllAdams = get(Human.class, "Adam");
However, the return value ConcurrentSkipListMap<String, <T extends MyObject>> gives compilation error, but <T extends MyObject>T is ok. What should I write instead?
Make these changes
class MyObject{}
class Human extends MyObject{}
public <T extends MyObject> ConcurrentSkipListMap<String, T> get(Class<T> clazz, Object... vars){
//...
return null;
}
//use the get() with
void hello(){
ConcurrentSkipListMap<String, Human> x = get(Human.class, "Adam");
}
I declared <T extends MyObject> before the return type and also added a variable x to hold the return value of the get function.
From the Spring reference documentation, a converter factory can be implemented as follows:
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
However, Enum is a raw type here. If I parameterize Enum and have my IDE (Eclipse Mars) add the method, it results in the following:
final class StringToEnumConverterFactory<T extends Enum<T>> implements ConverterFactory<String, Enum<T>> {
#Override
public <T extends Enum<T>> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter<T>(targetType);
}
private final class StringToEnumConverter<T extends Enum<T>> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return Enum.valueOf(this.enumType, source.trim());
}
}
}
But now I have two issues:
The following compiler error appears:
The type StringToEnumConverterFactory<T> must implement the inherited abstract method ConverterFactory<String,Enum<T>>.getConverter(Class<T>)
The type parameter T is hiding the type T.
Can you someone please explain how to properly change the StringToEnumConverterFactory to have Enum parameterized?
This actually turns out to be (perhaps surprisingly, not sure) a pain in the neck, because of the way Enum.valueOf is declared.
To get the example as-is to compile, you'd end up with something like this:
class StringToEnumConverterFactory<T extends Enum<T>>
implements ConverterFactory<String, T> {
// ^
#Override
public <E extends T> Converter<String, E> getConverter(Class<E> targetType) {
// ^^^^^^^^^^^ ^ ^
return new StringToEnumConverter<E>(targetType);
// ^
}
// ...
}
This, unfortunately, doesn't actually help us out because then you can only ever have e.g. a StringToEnumConverterFactory<Planet> or StringToEnumConverterFactory<Color> which defeats the purpose of the generalization the generic method offers. And you won't be able to make a StringToEnumConverterFactory<?> or StringToEnumConverter<Enum<?>>.
We don't actually want a type variable declared on the class, so the declaration we desire is like this, with a wildcard:
class StringToEnumConverterFactory
implements ConverterFactory<String, Enum<?>> {
#Override
public <E extends Enum<?>> Converter<String, E> getConverter(
Class<E> targetType) {
return new StringToEnumConverter<E>(targetType);
}
// ...
}
But this creates problems for us when we try to call Enum.valueOf because its type variable declaration is more restrictive. We won't ever be able to call it without some sort of cowboy unchecked cast (which is provably safe, but only with Enum):
return (T) Enum.valueOf((Class) enumType, source);
Fortunately, we can still otherwise bypass this through the Class object:
for (T constant : enumType.getEnumConstants())
if (constant.name().equals(source))
return constant;
The final code would be something like this:
class StringToEnumConverterFactory
implements ConverterFactory<String, Enum<?>> {
#Override
public <E extends Enum<?>> Converter<String, E> getConverter(
Class<E> targetType) {
return new StringToEnumConverter<E>(targetType);
}
static class StringToEnumConverter<E extends Enum<?>>
implements Converter<String, E> {
Class<E> enumType;
StringToEnumConverter(Class<E> enumType) {
this.enumType = enumType;
}
#Override
public E convert(String source) {
source = source.trim();
// perhaps we would cache the constants somewhere
for (E constant : enumType.getEnumConstants())
if (constant.name().equals(source))
return constant;
// also some people like to return null
throw new IllegalArgumentException(source);
}
}
}
So no raw types, no unchecked casts, but kind of a pain in the neck.
I think it may not be possible to define a converter factory as you are trying to do. The type you really want is a ConverterFactory<String, R>, where R extends Enum<R>. Note that I have used the type R here to match the documentation, in order to avoid confusion.
The API of the ConverterFactory interface then requires us to implement the following method:
public <T extends R> Converter<String, T> getConverter(Class<T> targetType) {
???
}
We know that it is in fact impossible to extend enum types, and therefore the only possibility is that T and R are the same type. However this is not known by the compiler, and therefore we cannot make any other assertions about the type T other than that it extends R.
In particular, we do not know whether T extends Enum<T>. We therefore cannot use T in a StringToEnumConverter<T> as you have defined it, since we cannot use Class<T> in Enum.valueOf.
I think you therefore need to continue using the original version without generics. It is slightly unfortunate that it requires an unchecked cast, but keep in mind that generics are erased at compile time, so all that will be left is a cast to Enum anyway.
I have a variable such as
Function<T, String> myFn;
I want to use it to get the class of type T.
For example, if we have
Function<Integer, String> myFn;
I need to get Integer.class.
I know that if we have
Integer x;
We can get Integer.class using x.getClass()
EDIT: Can we do this without using reflection?
EDIT: A more complete example
class MyFnClass<T> {
Function<T, String> myFn;
...
public Class<T> getTypeClass() {
???
}
}
Note that it doesn't have to use myFn, but since we cannot do T.class or something, I thought myFn is the only way to get this. Solutions that don't use myFn in this case are also acceptable.
Java uses type erasure meaning that the type information related to generics is removed at compile time.
It is therefore not possible to get the type T from Function<T, String> at runtime.
In the code snippet you include, you can could do this:
public abstract class AbstractFunction<T>
{
private Function<T, String> function;
...
public Class<T> getInputType() throws ClassNotFoundException
{
return (Class<T>)Class.forName(((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName());
}
}
public IntegerFunction extends AbstractFunction<Integer>
{
}
While all very tricksy, it is imho unnecessarily complex and can reduced to:
public abstract class AbstractFunction<T>
{
private Function<T, String> function;
...
public abstract Class<T> getInputType();
}
public IntegerFunction extends AbstractFunction<Integer>
{
#Override
public Class<Integer> getInputType()
{
return Integer.class;
}
}
This still introduces a rather pointless class, which can be reduced further to:
public class MyFunction<T>
{
private Class<T> inputType;
private Function<T, String> function;
public MyFunction(Class<T> inputType, Function<T, String> function)
{
this.inputType = inputType;
this.function = function;
}
public Class<T> getInputType()
{
return inputType;
}
}
All that said, needing to know the generic type is a bit of a code-smell and may indicated that there's something else not quite right in your design.
We can access the class from a variable like this:
myLocalVar = myClass.getMyVarVal();
Please refer this case as below :
public class myClass<T> {
protected Class<T> clazz;
public myClass() {
this.clazz = (Class<>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
public Class<T> getClazz() {
return clazz;
}
}
public class Fetch extends myClass<C> {
/* ... */
public void fetchClassFromVariable() {
//here we can call getClazz() to get the class from variable
}
}
How to get all elements of a list by instance?
I have a list that can have any class implementation of an interface Foo:
interface Foo;
class Bar implements Foo;
I want to use the java8 stream api to provide a utility method for extracting all elements of a specific class type:
public static <T extends Foo> List<T> getFromList(List<Foo> list, Class<T> type) {
return (List<T>) list.stream().filter(entry -> type.isInstance(entry)).collect(Collectors.toList());
}
using:
List<Foo> list;
List<Bar> bars = Util.getFromList(list, Bar.class);
Result: It works, but I have to add #SuppressWarnings due to the unchecked cast of (List<T>). How can I avoid this?
Introducing another type parameter that extends S is correct, however, in order to have the result as List<S>, but not as List<T>, you have to .map() the entries that pass the type::isInstance predicate to S.
public static <T extends Foo, S extends T> List<S> getFromList(List<T> list, Class<S> type) {
return list.stream()
.filter(type::isInstance)
.map(type::cast)
.collect(Collectors.toList());
}
As suggested by #Eran, this can be even simplified to work with just one type parameter:
public static <T extends Foo> List<T> getFromList(List<Foo> list, Class<T> type) {
return list.stream()
.filter(type::isInstance)
.map(type::cast)
.collect(Collectors.toList());
}
This seems to work without warnings :
public static <T extends Foo> List<T> getFromList(List<Foo> list, Class<T> type) {
return list.stream()
.filter(entry -> type.isInstance(entry))
.map(entry->type.cast(entry))
.collect(Collectors.toList());
}
Tested with Number replacing Foo and Integer replacing Bar :
public static <T extends Number> List<T> getFromList(List<Number> list, Class<T> type) {
return list.stream().filter(entry -> type.isInstance(entry)).map(entry->type.cast(entry)).collect(Collectors.toList());
}
public static void main(String[] args)
{
List<Number> list = new ArrayList<>();
list.add(5);
list.add(3.4);
list.add(7);
List<Integer> bars = getFromList(list, Integer.class);
System.out.println(bars);
}
Output:
[5, 7]
As list and type are not of the same type but rather in a inheritance hierarchy relation, you will likely to add another type argument similar to the following:
public static <T extends Foo, S extends T> List<T> getFromList(List<T> list, Class<S> type) {
return list.stream().filter(type::isInstance).collect(Collectors.toList());
}
Use Case
I currently have this pattern in many adapters:
entries.stream()
.filter(Entry.class::isInstance)
.map(Entry.class::cast)
.map(Entry::getFooBar)
.collect(Collectors.toList());
Where entries is a List of objects that implement a particular interface. Unfortunately, the interface - which is a part of a 3rd party library - does not define the common getters. To create a list of the objects I want, I need to search for them, cast them, and call the appropriate getter method.
I was going to refactor it into a helper class as such:
public static <T, O> List<O> entriesToBeans(List<T> entries,
Class<T> entryClass, Supplier<O> supplier) {
return entries.stream()
.filter(entryClass::isInstance)
.map(entryClass::cast)
.map(supplier) // <- This line is invalid
.collect(Collectors.toList());
}
I would then call this method to do the conversion:
Helper.entriesToBeans(entries,
Entry_7Bean.class,
Entry_7Bean::getFooBar);
Unfortunately, I can't pass the getter into the refactored function and have the map call it because the map is expecting a function.
Question
How can the getter be called in the refactored version?
A method like:
class T {
public O get() { return new O(); }
}
will map to a Function<T, O>.
So you can simply need to change your method signature to:
public static <T, O> List<O> entriesToBeans(List<T> entries,
Class<T> entryClass, Function<T, O> converter) {
Update: the reason for the cast, I suspect, is that your original list may contain elements that are not Ts. So you could also change the signature to:
public static <T, O> List<O> entriesToBeans(List<?> entries,
Class<T> entryClass, Function<T, O> converter) {
You can then pass a List<Object>, for example, and only keep the Ts in the list, cast and convert.
For reference, here is a working example (prints John, Fred):
static class Person {
private final String name;
Person(String name) { this.name = name; }
String name() { return name; }
}
public static void main(String[] args) {
List<String> result = entriesToBeans(Arrays.asList(new Person("John"), new Person("Fred")),
Person.class, Person::name);
System.out.println("result = " + result);
}
public static <T, O> List<O> entriesToBeans(List<?> entries,
Class<T> entryClass, Function<T, O> converter) {
return entries.stream()
.filter(entryClass::isInstance)
.map(entryClass::cast)
.map(converter)
.collect(Collectors.toList());
}
You should pass a Function<T, O> instead:
public static <T, O> List<O> entriesToBeans(List<T> entries, Class<T> entryClass,
Function<T, O> mapper) {
return entries.stream().filter(entryClass::isInstance)
.map(entryClass::cast).map(mapper)
.collect(Collectors.toList());
}