Sahibim;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Listenin genel türünü almanın (kolay) bir yolu var mı?
Sahibim;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Listenin genel türünü almanın (kolay) bir yolu var mı?
Yanıtlar:
Bunlar aslında belirli bir sınıfın alanlarıysa, o zaman onları biraz yansıma yardımı ile alabilirsiniz:
package test;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
public class Test {
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
public static void main(String... args) throws Exception {
Field stringListField = Test.class.getDeclaredField("stringList");
ParameterizedType stringListType = (ParameterizedType) stringListField.getGenericType();
Class<?> stringListClass = (Class<?>) stringListType.getActualTypeArguments()[0];
System.out.println(stringListClass); // class java.lang.String.
Field integerListField = Test.class.getDeclaredField("integerList");
ParameterizedType integerListType = (ParameterizedType) integerListField.getGenericType();
Class<?> integerListClass = (Class<?>) integerListType.getActualTypeArguments()[0];
System.out.println(integerListClass); // class java.lang.Integer.
}
}
Bunu parametre türleri ve dönüş yöntemleri için de yapabilirsiniz.
Ancak, onlar hakkında bilmeniz gereken sınıfın / yöntemin aynı kapsamı içindeyse, onları bilmenin bir anlamı yoktur, çünkü onları zaten kendiniz beyan etmişsinizdir.
<?>
yerine (joker karakter türü) kullanıyor <Class>
. Senin yerine <?>
biriminin by <Class>
ya da her neyse <E>
.
Aynı şeyi yöntem parametreleri için de yapabilirsiniz:
Type[] types = method.getGenericParameterTypes();
//Now assuming that the first parameter to the method is of type List<Integer>
ParameterizedType pType = (ParameterizedType) types[0];
Class<?> clazz = (Class<?>) pType.getActualTypeArguments()[0];
System.out.println(clazz); //prints out java.lang.Integer
Kısa cevap: hayır.
Bu muhtemelen bir kopyadır, şu anda uygun bir tane bulamıyor.
Java, tür silme adı verilen bir şey kullanır, bu da çalışma zamanında her iki nesnenin de eşdeğer olduğu anlamına gelir. Derleyici, listelerin tamsayı veya dize içerdiğini bilir ve bu nedenle bir tür güvenli ortamı koruyabilir. Bu bilgiler çalışma zamanında (nesne örneği bazında) kaybolur ve liste yalnızca 'Nesneler' içerir.
Sınıf hakkında ne tür parametreler olabileceğini biraz öğrenebilirsiniz, ancak normalde bu sadece "Nesne" yi genişleten herhangi bir şeydir, yani herhangi bir şey. Bir türü şu şekilde tanımlarsanız:
class <A extends MyClass> AClass {....}
AClass.class sadece A parametresinin MyClass tarafından sınırlandırıldığı gerçeğini içerecektir, ancak bundan daha fazlasını söylemenin bir yolu yoktur.
List<Integer>
ve olarak bildiriyor List<String>
; bu durumlarda tip silme geçerli değildir.
Bir koleksiyonun genel türü yalnızca içinde nesneler varsa önemli olmalıdır, değil mi? Yani bunu yapmak daha kolay değil:
Collection<?> myCollection = getUnknownCollectionFromSomewhere();
Class genericClass = null;
Iterator it = myCollection.iterator();
if (it.hasNext()){
genericClass = it.next().getClass();
}
if (genericClass != null) { //do whatever we needed to know the type for
Çalışma zamanında genel bir tür diye bir şey yoktur, ancak çalışma zamanında içerideki nesnelerin bildirilen genel ile aynı tür olması garanti edilir, bu nedenle işlemeden önce öğenin sınıfını test etmek yeterince kolaydır.
Yapabileceğiniz başka bir şey, listeyi doğru türdeki üyeleri almak, başkalarını görmezden gelmek (veya farklı şekilde işlemek) için işlemektir.
Map<Class<?>, List<Object>> classObjectMap = myCollection.stream()
.filter(Objects::nonNull)
.collect(Collectors.groupingBy(Object::getClass));
// Process the list of the correct class, and/or handle objects of incorrect
// class (throw exceptions, etc). You may need to group subclasses by
// filtering the keys. For instance:
List<Number> numbers = classObjectMap.entrySet().stream()
.filter(e->Number.class.isAssignableFrom(e.getKey()))
.flatMap(e->e.getValue().stream())
.map(Number.class::cast)
.collect(Collectors.toList());
Bu, sınıfları alt sınıfları olan Number
ve daha sonra istediğiniz gibi işleyebileceğiniz tüm öğelerin bir listesini verecektir . Öğelerin geri kalanı diğer listelere filtrelendi. Haritada oldukları için bunları istediğiniz gibi işleyebilir veya yoksayabilirsiniz.
Diğer sınıfların öğelerini tamamen göz ardı etmek istiyorsanız, çok daha basit hale gelir:
List<Number> numbers = myCollection.stream()
.filter(Number.class::isInstance)
.map(Number.class::cast)
.collect(Collectors.toList());
Bir listenin SADECE belirli bir sınıfla eşleşen öğeleri içermesini sağlamak için bir yardımcı program yöntemi bile oluşturabilirsiniz:
public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
return input.stream()
.filter(cls::isInstance)
.map(cls::cast)
.collect(Collectors.toList());
}
Object
ve listeden çıkarırsanız, türlerine göre uygun bir işleyiciye verirsiniz.
Bir alanın genel türünü bulmak için:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Döndürülen türün genel türünü almanız gerekiyorsa, döndürülen bir sınıftaki yöntemleri bulup Collection
sonra genel türlerine erişmem gerektiğinde bu yaklaşımı kullandım :
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.List;
public class Test {
public List<String> test() {
return null;
}
public static void main(String[] args) throws Exception {
for (Method method : Test.class.getMethods()) {
Class returnClass = method.getReturnType();
if (Collection.class.isAssignableFrom(returnClass)) {
Type returnType = method.getGenericReturnType();
if (returnType instanceof ParameterizedType) {
ParameterizedType paramType = (ParameterizedType) returnType;
Type[] argTypes = paramType.getActualTypeArguments();
if (argTypes.length > 0) {
System.out.println("Generic type is " + argTypes[0]);
}
}
}
}
}
}
Bu çıktılar:
Genel tür java.lang.String sınıfıdır
Steve K'nin cevabına genişleyen:
/**
* Performs a forced cast.
* Returns null if the collection type does not match the items in the list.
* @param data The list to cast.
* @param listType The type of list to cast to.
*/
static <T> List<? super T> castListSafe(List<?> data, Class<T> listType){
List<T> retval = null;
//This test could be skipped if you trust the callers, but it wouldn't be safe then.
if(data!=null && !data.isEmpty() && listType.isInstance(data.iterator().next().getClass())) {
@SuppressWarnings("unchecked")//It's OK, we know List<T> contains the expected type.
List<T> foo = (List<T>)data;
return retval;
}
return retval;
}
Usage:
protected WhateverClass add(List<?> data) {//For fluant useage
if(data==null) || data.isEmpty(){
throw new IllegalArgumentException("add() " + data==null?"null":"empty"
+ " collection");
}
Class<?> colType = data.iterator().next().getClass();//Something
aMethod(castListSafe(data, colType));
}
aMethod(List<Foo> foo){
for(Foo foo: List){
System.out.println(Foo);
}
}
aMethod(List<Bar> bar){
for(Bar bar: List){
System.out.println(Bar);
}
}
Çalışma zamanında, hayır, yapamazsınız.
Ancak yansıma yoluyla türü parametreleri vardır erişilebilir. Deneyin
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
Yöntem getGenericType()
bir Type nesnesi döndürür. Bu durumda, bu ParametrizedType
yöntemlerin getRawType()
( List.class
bu durumda içerecek olan ) sahip olduğu ve getActualTypeArguments()
bir dizi döndürecek (bu örnekte, uzunluk olarak String.class
veya birini içeren Integer.class
) bir örneği olacaktır .
method.getGenericParameterTypes()
Bir yöntemin parametrelerinin bildirilen türlerini almak için kullanabilirsiniz .
Aynı sorun vardı, ama bunun yerine instanceof kullandım. Bu şekilde mi:
List<Object> listCheck = (List<Object>)(Object) stringList;
if (!listCheck.isEmpty()) {
if (listCheck.get(0) instanceof String) {
System.out.println("List type is String");
}
if (listCheck.get(0) instanceof Integer) {
System.out.println("List type is Integer");
}
}
}
Bu, denetlenmeyen dökümleri kullanmayı içerir, bu nedenle bunu yalnızca bir liste olduğunu ve ne tür olabileceğini bildiğinizde yapın.
Diğerlerinin söylediği gibi, tek doğru cevap hayırdır, tür silinmiştir.
Listede sıfır olmayan sayıda öğe varsa, ilk öğenin türünü araştırabilirsiniz (örneğin, getClass yöntemini kullanarak). Bu size listenin genel türünü söylemez, ancak genel türün listedeki türlerin bazı üst sınıfları olduğunu varsaymak makul olacaktır.
Bu yaklaşımı savunmam, ancak bir bağda yararlı olabilir.
List<Integer>
ve olarak bildiriyor List<String>
; bu durumlarda tip silme geçerli değildir.
import org.junit.Assert;
import org.junit.Test;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class GenericTypeOfCollectionTest {
public class FormBean {
}
public class MyClazz {
private List<FormBean> list = new ArrayList<FormBean>();
}
@Test
public void testName() throws Exception {
Field[] fields = MyClazz.class.getFields();
for (Field field : fields) {
//1. Check if field is of Collection Type
if (Collection.class.isAssignableFrom(field.getType())) {
//2. Get Generic type of your field
Class fieldGenericType = getFieldGenericType(field);
//3. Compare with <FromBean>
Assert.assertTrue("List<FormBean>",
FormBean.class.isAssignableFrom(fieldGenericType));
}
}
}
//Returns generic type of any field
public Class getFieldGenericType(Field field) {
if (ParameterizedType.class.isAssignableFrom(field.getGenericType().getClass())) {
ParameterizedType genericType =
(ParameterizedType) field.getGenericType();
return ((Class)
(genericType.getActualTypeArguments()[0])).getSuperclass();
}
//Returns dummy Boolean Class to compare with ValueObject & FormBean
return new Boolean(false).getClass();
}
}
getFieldGenericTypefieldGenericType
bulunamadı
Tür silinir, böylece yapamazsınız. Bkz. Http://en.wikipedia.org/wiki/Type_erasure ve http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
List<Integer>
ve olarak bildiriyor List<String>
; bu durumlarda tip silme geçerli değildir.
Field
Bunları almak için Yansıma kullanın, sonra şunları yapabilirsiniz: field.genericType
genel hakkında da bilgi içeren türü almak için.