Tôi có;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Có cách nào (dễ dàng) để lấy loại chung của danh sách không?
Tôi có;
List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();
Có cách nào (dễ dàng) để lấy loại chung của danh sách không?
Câu trả lời:
Nếu đó thực sự là các trường của một lớp nhất định, thì bạn có thể nhận được chúng với một chút trợ giúp của sự phản ánh:
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.
}
}
Bạn cũng có thể làm điều đó cho các kiểu tham số và kiểu trả về của phương thức.
Nhưng nếu chúng nằm trong cùng một phạm vi của lớp / phương thức mà bạn cần biết về chúng, thì không có lý do gì để biết chúng, bởi vì bạn đã tự khai báo chúng.
<?>
(loại ký tự đại diện) thay vì <Class>
(loại lớp). Thay thế của bạn <?>
bằng <Class>
hoặc bất cứ điều gì <E>
.
Bạn cũng có thể làm tương tự cho các tham số phương thức:
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
Câu trả lời ngắn gọn: không.
Đây có lẽ là một bản sao, không thể tìm thấy một cái thích hợp ngay bây giờ.
Java sử dụng một cái gì đó gọi là kiểu xóa, có nghĩa là trong thời gian chạy cả hai đối tượng là tương đương. Trình biên dịch biết các danh sách chứa các số nguyên hoặc chuỗi và như vậy có thể duy trì một môi trường an toàn kiểu. Thông tin này bị mất (trên cơ sở đối tượng) trong thời gian chạy và danh sách chỉ chứa 'Đối tượng'.
Bạn CÓ THỂ tìm hiểu một chút về lớp học, loại nào có thể được tham số hóa, nhưng thông thường đây chỉ là bất cứ thứ gì mở rộng "Đối tượng", tức là bất cứ thứ gì. Nếu bạn xác định một loại là
class <A extends MyClass> AClass {....}
AClass. Class sẽ chỉ chứa thực tế là tham số A được giới hạn bởi MyClass, nhưng hơn thế nữa, không có cách nào để nói.
List<Integer>
và List<String>
; trong những trường hợp đó, loại tẩy không được áp dụng.
Kiểu chung của một bộ sưu tập chỉ nên quan trọng nếu nó thực sự có các đối tượng trong đó, phải không? Vì vậy, không dễ dàng hơn để làm:
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
Không có thứ gọi là loại chung trong thời gian chạy, nhưng các đối tượng bên trong thời gian chạy được đảm bảo cùng loại với loại chung được khai báo, do đó, chỉ cần kiểm tra lớp của vật phẩm trước khi chúng tôi xử lý nó là đủ dễ dàng.
Một điều khác bạn có thể làm chỉ đơn giản là xử lý danh sách để có được các thành viên đúng loại, bỏ qua những người khác (hoặc xử lý chúng khác nhau).
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());
Điều này sẽ cung cấp cho bạn một danh sách tất cả các mục có các lớp là các lớp con Number
mà sau đó bạn có thể xử lý khi bạn cần. Phần còn lại của các mục đã được lọc ra vào danh sách khác. Vì chúng ở trên bản đồ, bạn có thể xử lý chúng theo ý muốn hoặc bỏ qua chúng.
Nếu bạn muốn bỏ qua các mục của các lớp khác hoàn toàn, nó trở nên đơn giản hơn nhiều:
List<Number> numbers = myCollection.stream()
.filter(Number.class::isInstance)
.map(Number.class::cast)
.collect(Collectors.toList());
Bạn thậm chí có thể tạo một phương thức tiện ích để đảm bảo rằng danh sách CHỈ chứa các mục đó khớp với một lớp cụ thể:
public <V> List<V> getTypeSafeItemList(Collection<Object> input, Class<V> cls) {
return input.stream()
.filter(cls::isInstance)
.map(cls::cast)
.collect(Collectors.toList());
}
Object
và khi bạn rút chúng ra khỏi danh sách, bạn sẽ đưa chúng cho một trình xử lý thích hợp dựa trên loại của chúng.
Để tìm loại chung của một trường:
((Class)((ParameterizedType)field.getGenericType()).getActualTypeArguments()[0]).getSimpleName()
Nếu bạn cần lấy kiểu chung của kiểu trả về, tôi đã sử dụng cách tiếp cận này khi tôi cần tìm các phương thức trong một lớp trả về a Collection
và sau đó truy cập các kiểu chung của chúng:
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]);
}
}
}
}
}
}
Kết quả này:
Kiểu chung là lớp java.lang.String
Mở rộng câu trả lời của 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);
}
}
Trong thời gian chạy, không, bạn không thể.
Tuy nhiên thông qua sự phản ánh các tham số loại có thể truy cập. Thử
for(Field field : this.getDeclaredFields()) {
System.out.println(field.getGenericType())
}
Phương thức getGenericType()
trả về một đối tượng Type. Trong trường hợp này, nó sẽ là một thể hiện của ParametrizedType
, trong đó lần lượt có các phương thức getRawType()
(sẽ chứa List.class
, trong trường hợp này) và getActualTypeArguments()
sẽ trả về một mảng (trong trường hợp này, có độ dài một, chứa một String.class
hoặc Integer.class
).
method.getGenericParameterTypes()
để lấy các kiểu khai báo của tham số của phương thức.
Có cùng một vấn đề, nhưng tôi đã sử dụng instanceof thay thế. Đã làm theo cách này:
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");
}
}
}
Điều này liên quan đến việc sử dụng các phôi không được kiểm tra vì vậy chỉ làm điều này khi bạn biết nó là một danh sách, và nó có thể là loại gì.
Như những người khác đã nói, câu trả lời đúng duy nhất là không, loại đã bị xóa.
Nếu danh sách có số phần tử khác không, bạn có thể điều tra loại phần tử đầu tiên (ví dụ: sử dụng phương thức getClass). Điều đó sẽ không cho bạn biết loại chung của danh sách, nhưng sẽ hợp lý khi giả định rằng loại chung là một số siêu lớp của các loại trong danh sách.
Tôi sẽ không ủng hộ cách tiếp cận, nhưng trong một ràng buộc, nó có thể hữu ích.
List<Integer>
và List<String>
; trong những trường hợp đó, loại tẩy không được áp dụng.
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
nó không thể được tìm thấy
Loại bị xóa nên bạn sẽ không thể. Xem http://en.wikipedia.org/wiki/Type_erasure và http://en.wikipedia.org/wiki/Generics_in_Java#Type_erasure
List<Integer>
và List<String>
; trong những trường hợp đó, loại tẩy không được áp dụng.
Sử dụng Reflection để có được Field
những thứ này sau đó bạn chỉ có thể làm: field.genericType
để có được loại chứa thông tin chung chung.