Saya sudah;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Apakah ada cara (mudah) untuk mengambil tipe generik dari daftar?
Saya sudah;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Apakah ada cara (mudah) untuk mengambil tipe generik dari daftar?
Jawaban:
Jika itu benar-benar bidang kelas tertentu, maka Anda bisa mendapatkannya dengan sedikit bantuan refleksi:
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.
}
}
Anda juga dapat melakukan itu untuk tipe parameter dan mengembalikan jenis metode.
Tetapi jika mereka berada dalam ruang lingkup yang sama dari kelas / metode di mana Anda perlu tahu tentang mereka, maka tidak ada gunanya mengenal mereka, karena Anda sudah menyatakannya sendiri.
<?>
(tipe wildcard) alih-alih <Class>
(tipe kelas). Ganti <?>
dengan <Class>
atau apa pun <E>
.
Anda dapat melakukan hal yang sama untuk parameter metode juga:
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
Jawaban singkat: tidak.
Ini mungkin duplikat, tidak dapat menemukan yang tepat sekarang.
Java menggunakan sesuatu yang disebut tipe erasure, yang berarti pada saat runtime kedua objek sama. Kompiler tahu daftar berisi bilangan bulat atau string, dan dengan demikian dapat mempertahankan tipe lingkungan yang aman. Informasi ini hilang (berdasarkan instance objek) saat runtime, dan daftar hanya berisi 'Objek'.
Anda BISA mencari tahu sedikit tentang kelas, apa jenisnya dapat ditentukan oleh, tetapi biasanya ini hanya apa saja yang memperluas "Objek", yaitu apa saja. Jika Anda mendefinisikan tipe sebagai
class <A extends MyClass> AClass {....}
AClass.class hanya akan berisi fakta bahwa parameter A dibatasi oleh MyClass, tetapi lebih dari itu, tidak ada cara untuk mengatakannya.
List<Integer>
dan List<String>
; dalam kasus tersebut, tipe penghapusan tidak berlaku.
Jenis generik koleksi seharusnya hanya masalah jika itu benar-benar memiliki objek di dalamnya, bukan? Jadi bukankah lebih mudah melakukannya:
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
Tidak ada yang namanya tipe generik dalam runtime, tetapi objek di dalam saat runtime dijamin menjadi tipe yang sama dengan generik yang dideklarasikan, jadi cukup mudah hanya untuk menguji kelas item sebelum kita memprosesnya.
Hal lain yang dapat Anda lakukan adalah hanya memproses daftar untuk mendapatkan anggota yang tipe yang tepat, mengabaikan orang lain (atau memprosesnya secara berbeda).
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());
Ini akan memberi Anda daftar semua item yang kelasnya adalah subclass Number
yang kemudian dapat Anda proses sesuai kebutuhan. Sisa item disaring ke daftar lain. Karena mereka ada di peta, Anda dapat memprosesnya sesuai keinginan, atau mengabaikannya.
Jika Anda ingin mengabaikan item dari kelas lain sekaligus, itu menjadi jauh lebih sederhana:
List<Number> numbers = myCollection.stream()
.filter(Number.class::isInstance)
.map(Number.class::cast)
.collect(Collectors.toList());
Anda bahkan dapat membuat metode utilitas untuk memastikan bahwa daftar berisi HANYA item yang cocok dengan kelas tertentu:
public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
return input.stream()
.filter(cls::isInstance)
.map(cls::cast)
.collect(Collectors.toList());
}
Object
dan ketika Anda menariknya keluar dari daftar, Anda akan memberikannya kepada penangan yang tepat berdasarkan jenisnya.
Untuk menemukan tipe generik dari satu bidang:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Jika Anda perlu mendapatkan tipe generik dari tipe yang dikembalikan, saya menggunakan pendekatan ini ketika saya perlu menemukan metode di kelas yang mengembalikan a Collection
dan kemudian mengakses tipe generiknya:
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]);
}
}
}
}
}
}
Output ini:
Jenis generik adalah kelas java.lang.String
Memperluas jawaban Steve K:
/**
* 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);
}
}
Saat runtime, tidak, Anda tidak bisa.
Namun melalui refleksi parameter tipe dapat diakses. Mencoba
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
Metode getGenericType()
mengembalikan objek Type. Dalam hal ini, ini akan menjadi turunan dari ParametrizedType
, yang pada gilirannya memiliki metode getRawType()
(yang akan berisi List.class
, dalam kasus ini) dan getActualTypeArguments()
, yang akan mengembalikan array (dalam hal ini, panjang satu, berisi salah satu String.class
atau Integer.class
).
method.getGenericParameterTypes()
untuk mendapatkan tipe parameter metode yang dideklarasikan.
Punya masalah yang sama, tapi saya menggunakan instanceof. Apakah dengan cara ini:
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");
}
}
}
Ini melibatkan penggunaan gips yang tidak dicentang sehingga hanya melakukan ini ketika Anda tahu itu adalah daftar, dan apa jenisnya.
Umumnya tidak mungkin, karena List<String>
danList<Integer>
berbagi kelas runtime yang sama.
Anda mungkin dapat merefleksikan tipe yang dideklarasikan dari field yang menyimpan daftar, meskipun (jika tipe yang dideklarasikan itu sendiri tidak merujuk pada parameter tipe yang nilainya tidak Anda ketahui).
Seperti yang dikatakan orang lain, satu-satunya jawaban yang benar adalah tidak, tipe telah dihapus.
Jika daftar memiliki jumlah elemen yang tidak nol, Anda bisa menyelidiki tipe elemen pertama (misalnya menggunakan metode getClass). Itu tidak akan memberi tahu Anda tipe generik dari daftar, tetapi akan masuk akal untuk menganggap bahwa tipe generik adalah beberapa superclass dari tipe dalam daftar.
Saya tidak akan menganjurkan pendekatan itu, tetapi mungkin itu berguna.
List<Integer>
dan List<String>
; dalam kasus tersebut, tipe penghapusan tidak berlaku.
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
itu tidak dapat ditemukan
Jenis ini dihapus sehingga Anda tidak akan bisa. Lihat http://en.wikipedia.org/wiki/Type_erasure dan http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
List<Integer>
dan List<String>
; dalam kasus tersebut, tipe penghapusan tidak berlaku.
Gunakan Reflection untuk mendapatkan Field
ini maka Anda bisa melakukan: field.genericType
untuk mendapatkan jenis yang berisi informasi tentang generik juga.