I'm facing an issue with generic types:
public static class Field<T> {
private Class<? extends T> clazz;
public Field(Class<? extends T> clazz) {
this.clazz = clazz;
}
}
public static void main(String[] args) {
// 1. (warning) Iterable is a raw type. References to generic type Iterable<T> should be parameterized.
new Field<Iterable>(List.class);
// 2. (error) The constructor Main.Field<Iterable<?>>(Class<List>) is undefined.
new Field<Iterable<?>>(List.class);
// 3. (error) *Simply unpossible*
new Field<Iterable<?>>(List<?>.class);
// 4. (warning) Type safety: Unchecked cast from Class<List> to Class<? extends Iterable<?>>.
new Field<Iterable<?>>((Class<? extends Iterable<?>>) List.class);
}
What's the best solution between the 1. and the 4. (or any other one by the way)?
public class Field <T> {
private Class <? extends T> clazz;
public <TT extends T> Field (Class <TT> clazz) {
this.clazz = clazz;
}
public static void main (String [] args) {
new Field <Iterable <?>> (List.class);
}
}
Related
I am trying to implement ConverterFactory for all enums. I wrote
public class StringToEnumConverterFactory implements ConverterFactory<String, Enum<?>> {
#Override
public <T extends Enum<?>> #NotNull Converter<String, T> getConverter(#NotNull Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private static class StringToEnumConverter<T extends Enum<T>> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return Enum.valueOf(this.enumType, source.trim());
}
}
}
Unfortunately, checker warns line
return new StringToEnumConverter(targetType);
with
Raw use of parameterized class 'StringToEnumConverter'
Unchecked assignment: 'StringToEnumConverterFactory.StringToEnumConverter' to 'org.springframework.core.convert.converter.Converter<java.lang.String,T>'
Unchecked call to 'StringToEnumConverter(Class<T>)' as a member of raw type 'StringToEnumConverterFactory.StringToEnumConverter'
Why? Is it possible to satisfy it?
I am trying to build a service Locator using generics and I'm getting warnings by the compiler at
objects.put(klass, value)
It says incompatible types, Required: T. Found: T.
public class SimpleServiceLocator <T> implements ServiceLocator<T> {
private HashMap<Class, Factory> services = new HashMap<>();
private HashMap<Class, T> objects = new HashMap<>();
#Override
public <T> void setService(Class<T> klass, Factory<T> factory) throws LocatorError {
if (services.containsKey(klass)) throw new LocatorError();
services.put(klass, factory);
}
#Override
public <T> T getObject(Class<T> klass) throws LocatorError {
if (services.containsKey(klass)) {
Factory factory = services.get(klass);
return (T) factory.create(this);
} else if(objects.containsKey(klass)) {
return (T) objects.get(klass);
}
throw new LocatorError();
}
#Override
public <T> void setConstant(Class<T> klass, T value) throws LocatorError {
if (objects.containsKey(klass)) throw new LocatorError();
objects.put(klass, value);
}
}
And the Interface is:
public interface ServiceLocator <T>{
<T> void setService(Class<T> klass, Factory<T> factory)
throws LocatorError;
<T> void setConstant(Class<T> klass, T value)
throws LocatorError;
<T> T getObject(Class<T> klass)
throws LocatorError;
}
You have defined multiple generic types with the same name.
public class SimpleServiceLocator <T>
defines a generic type T at the class level
<T> void setService
defines a generic type T at the function level
If you use T inside your functions, you will use the T from your function definition, which is different from the type you used to declare your HashMap
I have this Java code:
public abstract class BaseObject implements Serializable {
...
}
public interface MessageConverter {
<T extends BaseObject> T getMessage(String message, Class<T> requiredType);
}
public abstract class AbstractMessageConverter implements MessageConverter {
...
}
public class WebMessageConverter extends AbstractMessageConverter {
public <T extends BaseObject> T getMessage(String message, Class<T> requiredType) {
final T data;
...
return data;
}
}
public class Utils {
private static <T> T getItemType(String value, Class<T> requiredType, MessageConverter messageConverter) throws Exception
{
if (BaseObject.class.isAssignableFrom(requiredType))
{
return (T) messageConverter.getMessage(value, requiredType);
}
return (T) toRequiredType(value, requiredType);
}
public static <T> T toRequiredType(Object value, Class<T> requiredType)
{
final T data;
...
return data;
}
}
The following line:
return (T) messageConverter.getMessage(value, requiredType);
Causes:
The method getMessage(String, Class<T extends BaseObject>)
in the type MessageConverter is not applicable for the arguments (String, Class<T>)
how to fix this error?
The signature on WebMessageConverter expects a subtype of BaseObject when it declares <T extends BaseObject>.
The signature on the Utils has no such restriction as it uses a plain <T>, it would e.g. allow Class<String> to be used. That's why the compiler complains.
To resolve, declare the methods in Utils like this:
private static <T extends BaseObject> T getItemType(String value, Class<T> requiredType, MessageConverter messageConverter) throws Exception
Is is possible to have a generic return type such that collection can built with interface as well as implementation.
public static void main(String[] args) {
List<Test1> list = ImmutableList.of(new Test1());
List<ITest> test_return_1 = buildIntRange(list);
List<Test1> test_return_2 = buildIntRange(list); //error
}
private static <K extends ITest> List<ITest> buildIntRange(List<K> test) {
return ImmutableList.copyOf(test);
}
Sure. Use the same signature as ImmutableList.copyOf, or just use ImmutableList.copyOf directly:
static <T> List<T> copyOf(Iterable<? extends T> collection)
I am trying to do reverse lookup on few enum classes implementing same Field interface by iterating through list of Classes using Guava's Maps.uniqueIndex:
Field valueOfSearchName = null;
for (final Class<? extends Enum<?>> clazz : ImmutableList.of(
EntityField.class,
AddressField.class,
PersonFunctionType.class)) {
valueOfSearchName = Fields.valueOfSearchName(clazz, term.field()); // error
if (valueOfSearchName != null) {
// do something...
break;
}
}
I don't want to repeat same code (for making index and doing lookup) in all enum classes, so I use helper static class Fields containing Fields.valueOfSearchName method:
public static <E extends Enum<E> & Field> Field valueOfSearchName(
final Class<E> clazz, final String searchName) {
// TODO: cache the index
final ImmutableMap<String, E> index = Maps.uniqueIndex(
EnumSet.allOf(clazz), GET_SEARCH_NAME_FUNCTION);
return index.get(searchName);
}
Unfortunately, Eclipse shows an error:
Bound mismatch:
The generic method valueOfSearchName(Class<E>, String) of type Fields is not
applicable for the arguments (Class<capture#1-of ? extends Enum<?>>, String).
The inferred type capture#1-of ? extends Enum<?> is not a valid substitute
for the bounded parameter <E extends Enum<E> & Field>
The problem is Class<? extends Enum<?>> clazz in for-each loop (not matching Field), but I don't know how to deal with this case (obviously I cannot add & Field to clazz).
Consider Class<? extends List<?>. Class<? extends List<?> has two wildcards whereas <E extends List<E>> Class<E> only has generic parameter. The former will admit Class<ArrayList<String>>. So without doing something extra special for enums, the types are not compatible.
How to fix? An extra layer of indirection!
public final class MetaEnum<E extends Enum<E>> {
private final E clazz;
public static <E extends Enum<E>> MetaEnum<E> of(E clazz) {
return clazz;
}
private MetaEnum(E clazz) {
this.clazz = clazz;
}
public E clazz() {
return clazz;
}
// ...
}
for (final MetaEnum<?> meta : ImmutableList.of(
MetaEnum.of(EntityField .class),
MetaEnum.of(AddressField .class),
MetaEnum.of(PersonFunctionType.class)
)) {
Field valueOfSearchName = Fields.valueOfSearchName(
meta.clazz(), term.field()
);
...
(Usual Stack Overflow dislaimer: Not so much as attempted to compile.)
Inspired by Tom Hawtin's answer I created wrapper class holding Classes, but only those with signature <E extends Enum<E> & Field>:
public final static class FieldEnumWrapper<E extends Enum<E> & Field> {
private final Class<E> clazz;
private final ImmutableMap<String, E> index;
public static <E extends Enum<E> & Field>
FieldEnumWrapper<E> of(final Class<E> clazz) {
return new FieldEnumWrapper<E>(clazz);
}
private FieldEnumWrapper(final Class<E> clazz) {
this.clazz = clazz;
this.index = Maps.uniqueIndex(
EnumSet.allOf(clazz), new Function<E, String>() {
#Override
public String apply(final E input) {
return input.searchName();
}
});
}
public Class<E> clazz() {
return clazz;
}
public Field valueOfSearchName(final String searchName) {
return index.get(searchName);
}
}
Now:
for (final FieldEnumWrapper<?> fieldEnum : ImmutableList.of(
FieldEnumWrapper.of(EntityField.class),
FieldEnumWrapper.of(AddressField.class),
FieldEnumWrapper.of(PersonFunctionType.class))) {
valueOfSearchName = fieldEnum.valueOfSearchName("POD_I_OS_PARTNER");
// ...
is type-safe and inappropriate usage of FieldEnumWrapper's static factory:
FieldEnumWrapper.of(NotEnumAndFieldClass.class)
generates compile error.
Moreover, valueOfSearchName is now method of FieldEnumWrapper what make more sense that helper class.
maybe something like this:
import java.util.*;
class N {
static int n;
}
interface HasField {
int getField();
}
enum Color implements HasField {
r, g, b;
public int getField() {
return field;
}
private int field = N.n++;
}
enum Day implements HasField {
m, t, w, th, f, sa, su;
public int getField() {
return field;
}
private int field = N.n++;
}
class Helper {
Helper(Set<HasField> set) {
for (HasField hasField : set)
if (hasField instanceof Enum) {
Enum<?> e = (Enum<?>) hasField;
for (Object o : e.getDeclaringClass().getEnumConstants()) {
map.put(((HasField) o).getField(), (Enum<?>) o);
}
} else
throw new RuntimeException(hasField + " is not an enum!");
}
final Map<Integer, Enum<?>> map = new TreeMap<Integer, Enum<?>>();
}
public class Main {
public static void main(String[] args) {
Set<HasField> set = new LinkedHashSet<HasField>();
set.add(Color.r);
set.add(Day.m);
Helper helper = new Helper(set);
for (int i = 0; i < N.n; i++)
System.out.println(i + " " + helper.map.get(i));
}
}