我想在以下场景中直接转换为适当的列表类型.如果擦除会杀死类类型信息,那么在以下场景中维护类类型的最佳方法是什么?
class MyProperties {
private final Map<String, List<?>> properties = new HashMap<>();
public void put(final String key, final List<?> value) {
properties.put(key, value);
}
public <T> List<T> get(final String key, final Class<T> t) {
final List<?> object = properties.get(key);
// Here is where the issue lies because the class type is gone, so we
// will always skip this block and return null
if (object.getClass().isAssignableFrom(t)){
return (List<T>)object;
}
return null;
}
public static void main(String[] args){
List<String> myList = new ArrayList<>();
myList.add("Bacon");
myList.add("Eggs");
List<Integer> myList2 = new ArrayList<>();
myList2.add(1);
myList2.add(2);
MyProperties props = new MyProperties();
props.put("myKey", myList);
props.put("myKey2", myList2);
// I'd like to directly cast to the appropriate list type if possible
List<String> foo = props.get("myKey", String.class);
List<Integer> foo2 = props.get("myKey2", Integer.class);
}
}
Run Code Online (Sandbox Code Playgroud)
存储元素类型
您可以做的是传递列表元素的类并单独存储它:
private final Map<String, List<?>> properties = new HashMap<>();
private final Map<String, Class<?>> propClasses= new HashMap<>();
public <T> void put(final String key, final List<T> value, final Class<T> elementClass) {
properties.put(key, value);
propClasses.put(key, elementClass);
}
Run Code Online (Sandbox Code Playgroud)
正如您已经指出的,由于类型擦除,您不知道列表元素的类型.因此,您需要单独存储该类,因此需要第二个映射.
要确保传递正确的类,请更改put方法以获取类参数,该类参数必须与列表的泛型类型相同.但请注意,您仍然可以使用原始类型禁用类型检查.
例:
//works because the type is Integer for the list and the class
put( "working", new ArrayList<Integer>(), Integer.class );
//doesn't work since Number != Integer, even though Integer extends Number
put( "error", new ArrayList<Number>(), Integer.class );
put( "still an error", new ArrayList<Integer>(), Number.class );
//careful, this compiles because the raw type List is used
//the compiler would warn us though, so don't take those warnings lightly :)
put( "raw", (List)new ArrayList<Number>(), Integer.class );
Run Code Online (Sandbox Code Playgroud)
检索具有确切元素类型的列表
public <T> List<T> get(final String key, final Class<T> t) {
Class<?> propClass = propClasses.get( key );
if( propClass == null || t == null || !t.equals( propClass ) ) {
return null;
}
return (List<T>)properties.get(key);
}
Run Code Online (Sandbox Code Playgroud)
获取元素时,首先在相应的映射中获取类,并检查t类本身还是超类.如果成功,您将从属性映射中获取列表并将其转换为您需要的类型.
t.equals( propClass )因为你不应该将列表强制转换为超类型的元素.否则,您将能够插入错误类型的元素.
作为一个例子假设List<Integer>.以下会导致问题:
//assume this were allowed by your code
List<Number> l = get("working", Number.class );
l.add( new Double( 0.5 ) ); //ouch, now we have a double in an integer list
Run Code Online (Sandbox Code Playgroud)
检索元素类型为下限的列表(只读)
如果要使用元素类的超类,可以更改get()方法或添加"只读"方法,如下所示:
public <T> List<? extends T> getReadOnly(final String key, final Class<T> t) {
Class<?> propClass = propClasses.get( key );
if( propClass == null || t == null || !t.isAssignableFrom( propClass ) ) {
return null;
}
return (List<? extends T>)properties.get(key);
}
Run Code Online (Sandbox Code Playgroud)
如果T是元素类或超类,我们允许转换为List<? extends T>.因此编译器允许我们使用超类型访问元素,但不允许我们添加新元素 - 除非我们通过手动强制转换来破坏它.
例:
List<? extends Number> l = getReadOnly( "working", Number.class );
//won't compile since the compiler isn't sure about the type of the list
//in our case it would actually be a List<Integer> but the compiler doesn't know that
//and thus disallows adding to the list - which is good since we'd produce a bug here
l.add( new Double(0.5));
//this would compile due to the raw type, which essentially disables type checks here
//fortunately the compiler warns us not to do this or at least be very careful
((List)l).add( new Double(0.5));
Run Code Online (Sandbox Code Playgroud)