どうすればこの種のものを機能させることができますか?確認できますが、確認でき(obj instanceof List<?>)
ません(obj instanceof List<MyType>)
。これを行う方法はありますか?
9 に答える
ジェネリックスのコンパイル時にデータ型が消去されるため、これは不可能です。これを行う唯一の可能な方法は、リストが保持するタイプを保持するある種のラッパーを作成することです。
public class GenericList <T> extends ArrayList<T>
{
private Class<T> genericType;
public GenericList(Class<T> c)
{
this.genericType = c;
}
public Class<T> getGenericType()
{
return genericType;
}
}
if(!myList.isEmpty() && myList.get(0) instanceof MyType){
// MyType object
}
おそらく、リフレクションを使用して、それらのタイプをチェックする必要があります。リストのタイプを取得するには: 一般的なタイプのjava.util.Listを取得します。
これは、空ではないobject
のインスタンスであることを確認する場合に使用できます。List<T>
if(object instanceof List){
if(((List)object).size()>0 && (((List)object).get(0) instanceof MyObject)){
// The object is of List<MyObject> and is not empty. Do something with it.
}
}
if (list instanceof List && ((List) list).stream()
.noneMatch((o -> !(o instanceof MyType)))) {}
ObjectのListまたはMap値の参照がコレクションのインスタンスであるかどうかを確認する場合は、必要なListのインスタンスを作成し、そのクラスを取得するだけです...
Set<Object> setOfIntegers = new HashSet(Arrays.asList(2, 4, 5));
assetThat(setOfIntegers).instanceOf(new ArrayList<Integer>().getClass());
Set<Object> setOfStrings = new HashSet(Arrays.asList("my", "name", "is"));
assetThat(setOfStrings).instanceOf(new ArrayList<String>().getClass());
これをジェネリックスでラップできない場合(@Martijnの回答)、冗長なリストの反復を避けるために、キャストせずに渡すことをお勧めします(最初の要素の型をチェックしても何も保証されません)。リストを反復処理するコードの各要素をキャストできます。
Object attVal = jsonMap.get("attName");
List<Object> ls = new ArrayList<>();
if (attVal instanceof List) {
ls.addAll((List) attVal);
} else {
ls.add(attVal);
}
// far, far away ;)
for (Object item : ls) {
if (item instanceof String) {
System.out.println(item);
} else {
throw new RuntimeException("Wrong class ("+item .getClass()+") of "+item );
}
}
instanceofを使用する代わりに、偽のファクトリを使用して多くのメソッドを含めることができます。
public class Message1 implements YourInterface {
List<YourObject1> list;
Message1(List<YourObject1> l) {
list = l;
}
}
public class Message2 implements YourInterface {
List<YourObject2> list;
Message2(List<YourObject2> l) {
list = l;
}
}
public class FactoryMessage {
public static List<YourInterface> getMessage(List<YourObject1> list) {
return (List<YourInterface>) new Message1(list);
}
public static List<YourInterface> getMessage(List<YourObject2> list) {
return (List<YourInterface>) new Message2(list);
}
}
ここでの主な懸念は、コレクションが定義内の型を保持しないことです。タイプは実行時にのみ使用できます。複雑なコレクションをテストする関数を思いつきました(ただし、1つの制約があります)。
オブジェクトがジェネリックコレクションのインスタンスであるかどうかを確認します。コレクションを表すために、
- クラスはありません、常に
false
- 1つのクラス、それはコレクションではなく、
instanceof
評価の結果を返します List
またはを表すためSet
に、リストのタイプが次に来ます。例:{List、Integer} forList<Integer>
- を表すため
Map
に、キーと値のタイプが次に来ます。たとえば、{Map、String、Integer} forMap<String, Integer>
同じルールを使用して、より複雑なユースケースを生成できます。たとえば、を表すために、次のList<Map<String, GenericRecord>>
ように呼び出すことができます
Map<String, Integer> map = new HashMap<>();
map.put("S1", 1);
map.put("S2", 2);
List<Map<String, Integer> obj = new ArrayList<>();
obj.add(map);
isInstanceOfGenericCollection(obj, List.class, List.class, Map.class, String.class, GenericRecord.class);
この実装は、マップ内のネストされた型をサポートしていないことに注意してください。したがって、キーと値のタイプは、コレクションではなくクラスである必要があります。しかし、それを追加するのは難しいことではありません。
public static boolean isInstanceOfGenericCollection(Object object, Class<?>... classes) {
if (classes.length == 0) return false;
if (classes.length == 1) return classes[0].isInstance(object);
if (classes[0].equals(List.class))
return object instanceof List && ((List<?>) object).stream().allMatch(item -> isInstanceOfGenericCollection(item, Arrays.copyOfRange(classes, 1, classes.length)));
if (classes[0].equals(Set.class))
return object instanceof Set && ((Set<?>) object).stream().allMatch(item -> isInstanceOfGenericCollection(item, Arrays.copyOfRange(classes, 1, classes.length)));
if (classes[0].equals(Map.class))
return object instanceof Map &&
((Map<?, ?>) object).keySet().stream().allMatch(classes[classes.length - 2]::isInstance) &&
((Map<?, ?>) object).values().stream().allMatch(classes[classes.length - 1]::isInstance);
return false;
}