Nhận loại chung của lớp khi chạy


508

Làm thế nào tôi có thể đạt được điều này?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Tất cả mọi thứ tôi đã thử cho đến nay luôn trả về loại Objectchứ không phải loại cụ thể được sử dụng.


3
@SamuelVidal Đây không phải là .NET, đây là Java.
Mặt trận

Câu trả lời:


317

Như những người khác đã đề cập, nó chỉ có thể thông qua sự phản ánh trong một số trường hợp nhất định.

Nếu bạn thực sự cần loại, đây là mẫu giải pháp (loại an toàn) thông thường:

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}

58
Tôi thích câu trả lời này nhưng nó hơi cồng kềnh khi khởi tạo: GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass. Class);
Eliseo Ocampos

1
Nó thậm chí còn dài dòng hơn nếu bạn sử dụng phương pháp dao / nhà máy / người quản lý. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj

2
Đó là sự thật, nhưng không hoạt động trong tất cả các trường hợp như đậu từ xa không trạng thái được khởi tạo bằng thùng chứa / phản xạ.
djmj

6
Cũng như một phần tiếp theo bình luận trước đây của tôi - sau nhiều nỗi đau chơi với sự phản chiếu, tôi đã kết thúc bằng cách sử dụng câu trả lời này.
Tomáš Zato - Phục hồi Monica

18
Bạn có thể có được xung quanh tham chiếu thừa bằng cách cung cấp một phương thức tĩnh chung. Một cái gì đó như public static <T> GenericClass<T> of(Class<T> type) {...}và sau đó gọi nó như vậy : GenericClass<String> var = GenericClass.of(String.class). Đẹp hơn một chút.
Joeri Hendrickx

262

Tôi đã thấy một cái gì đó như thế này

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

trong ví dụ ngủ đông GenericDataAccessObjects


84
Kỹ thuật này hoạt động trong đó tham số loại được xác định trên siêu lớp ngay lập tức, nhưng nó không thành công nếu tham số loại được xác định ở nơi khác trong cấu trúc phân cấp. Để xử lý các trường hợp phức tạp hơn, một số thứ như TypeTools có thể được sử dụng. Các tài liệu bao gồm một ví dụ về DAO chung tinh vi hơn.
Jonathan

25
Điều này chỉ trả về các tham số loại thực tế được sử dụng khi LỚP thực hiện / mở rộng một cái gì đó có khai báo chung, nó không trả về các tham số loại thực tế được sử dụng khi INSTANCE được khởi tạo. Nói cách khác, nó CÓ THỂ nói rằng trong class A implements Comparable<String>, tham số loại thực tế là String, nhưng nó KHÔNG THỂ nói rằng trong Set<String> a = new TreeSet<String>(), tham số loại thực tế là String. Trong thực tế, thông tin tham số loại bị "xóa" sau khi biên dịch, như được giải thích trong các câu trả lời khác.
Siu Ching Pong -Asuka Kenji- 18/1/2015

32
Tôi nhận được java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypecâu trả lời này.
Tomáš Zato - Phục hồi Monica

4
Cách tiếp cận này cũng có thể đạt được bằng cách sử dụng Class-Matetừ dân Jackson. Tôi đã viết một ý chính ở đây gist.github.com/yunspace/930d4d40a787a1f6a7d1
yunspace

5
@ TomášZato Gọi đơn giản là đoạn mã trên trả lại cùng một ngoại lệ cho tôi. Tôi biết rằng nó hơi muộn, nhưng dù sao, trong trường hợp của tôi, tôi đã phải gọi (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()để đi đến các đối số kiểu thực tế.
AndrewMcCoist

99

Generics không được thống nhất tại thời gian chạy. Điều này có nghĩa là thông tin không có mặt tại thời điểm chạy.

Thêm các khái quát vào Java trong khi duy trì khả năng tương thích ngược là một cách giải quyết (bạn có thể thấy bài báo quan trọng về nó: Làm cho tương lai an toàn cho quá khứ: thêm tính tổng quát vào ngôn ngữ lập trình Java ).

Có một tài liệu phong phú về chủ đề này, và một số người không hài lòng với tình trạng hiện tại, một số người nói rằng thực sự đó là một sự thu hút và không có nhu cầu thực sự cho nó. Bạn có thể đọc cả hai liên kết, tôi thấy chúng khá thú vị.


179
Tất nhiên chúng tôi không hài lòng, .NET có cơ chế xử lý chung tốt hơn nhiều
Pacerier

6
@Pacerier: nhưng các thế hệ thống nhất một mình sẽ không đưa Java đến mức .NET. Các loại giá trị một mã chuyên biệt cho những thứ đó ít nhất là quan trọng như nhau tại sao .NET tốt hơn trong khu vực chung.
Joachim Sauer

@JoachimSauer, có các loại giá trị. Tôi luôn muốn những người trong java. Btw bạn có ý nghĩa gì bởi mã chuyên ngành?
Pacerier

3
@ spaaarky21 Không, các tham số loại chung được loại bỏ trong quá trình biên dịch (cái gọi là "xóa", bạn có thể google nó). Thủ thuật trong câu trả lời của FrVaBe chỉ hoạt động nếu các thông số loại của siêu lớp được biết một cách tĩnh (xem phần cam kết đầu tiên của Johnathn)
ewernli

1
Xóa kiểu Java là một lỗ hổng thiết kế lịch sử; nhiều mã đã được viết để đi xung quanh nó hơn là viết để thực hiện nó.
Abhijit Sarkar

68

Sử dụng ổi.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

Một thời gian trước, tôi đã đăng một số ví dụ đầy đủ bao gồm các lớp trừu tượng và các lớp con ở đây .

Lưu ý: đây yêu cầu bạn khởi tạo một lớp con của GenericClassđể nó có thể ràng buộc các tham số kiểu một cách chính xác. Nếu không, nó sẽ chỉ trả về kiểu như T.


3
Lưu ý rằng tôi tạo một lớp con ẩn danh trống (xem hai dấu ngoặc nhọn ở cuối). Điều này sử dụng sự phản chiếu để chiến đấu với kiểu xóa thời gian chạy của Java. Bạn có thể tìm hiểu thêm tại đây: code.google.com/p/guava-lologists/wiki/ReflectionExplained
Cody A. Ray

3
@ CodyA.Ray Mã của bạn ném a java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Vì vậy, tôi đã thay đổi dòng new TypeToken(getClass()) { }thành new TypeToken<T>(getClass()) { }. Bây giờ, mã chạy tốt, nhưng Loại vẫn là 'T'. Xem điều này: gist.github.com/m-manu/9cda9d8f9d53bead2035
Manu Manjunath

3
@Dominik Vui lòng xem ví dụ cập nhật mà bạn có thể sao chép và dán để tự kiểm tra. Tôi cũng đã thêm một ghi chú làm rõ rằng bạn phải khởi tạo một lớp con (như được hiển thị). Như một nghi thức chung khuyên, xin vui lòng đọc bất kỳ bài viết được liên kết và javadocs liên quan trước khi bạn cáo buộc một poster "suy nghĩ mong muốn". Tôi đã sử dụng mã sản xuất tương tự nhiều lần. Những người trợ giúp Guava mà tôi đang trình diễn được dành cho trường hợp sử dụng chính xác này và javadocs của họ hiển thị gần như một câu trả lời chính xác cho câu hỏi này. docs.guava-lologists.googlecode.com/git/javadoc/com/google/ mẹo
Cody A. Ray

1
Câu trả lời này hoạt động rất tốt với Java8 - Tôi có một giao diện và lớp nhà máy để phát ra nhiều loại khác nhau và muốn người dùng có thể dễ dàng xác định loại. Trong giao diện của tôi, tôi đã thêm phương thức mặc định: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand

2
@ CodyA.Ray Vì điều này chỉ hoạt động với các lớp con của GenericClass, bạn nên làm cho lớp abstractđó để sử dụng sai không biên dịch.
Martin

37

Chắc chắn, bạn có thể.

Java không sử dụng thông tin trong thời gian chạy, vì lý do tương thích ngược. Nhưng thông tin thực sự hiện diện dưới dạng siêu dữ liệu và có thể được truy cập thông qua sự phản chiếu (nhưng nó vẫn không được sử dụng để kiểm tra kiểu).

Từ API chính thức:

http://doad.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArgument%28%29

Tuy nhiên , đối với kịch bản của bạn, tôi sẽ không sử dụng sự phản chiếu. Cá nhân tôi có xu hướng sử dụng nó cho mã khung. Trong trường hợp của bạn, tôi chỉ cần thêm kiểu như là một hàm tạo.


10
getActualTypeArgument chỉ trả về các đối số kiểu cho lớp ngay lập tức. Nếu bạn có một hệ thống phân cấp loại phức tạp trong đó T có thể được tham số hóa ở bất kỳ đâu trong cấu trúc phân cấp, bạn sẽ cần thực hiện một chút công việc để tìm ra nó là gì. Đây là ít nhiều những gì TypeTools làm.
Jonathan

36

Java genericics chủ yếu là thời gian biên dịch, điều này có nghĩa là thông tin kiểu bị mất khi chạy.

class GenericCls<T>
{
    T t;
}

sẽ được biên dịch thành một cái gì đó như

class GenericCls
{
   Object o;
}

Để có được thông tin kiểu trong thời gian chạy, bạn phải thêm nó làm đối số của ctor.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

Thí dụ:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;

9
private final Class<T> type;
naXa

Làm cách nào tôi có thể tạo một kiểu mảng từ nó:Type t = //String[]
Pawel Cioch

@PawelCioch java.lang.reflect.Array.newInstance (phần tử, độ dài); hy vọng điều này sẽ giúp (javadoc có thể được tìm thấy ở đây docs.oracle.com/javase/8/docs/api/java/lang/reflect/... )
josefx

@PawelCioch đã bỏ lỡ một .getClass () để lấy kiểu từ mảng đã tạo. Dường như không có cách nào trực tiếp để có được một lớp mảng. Hầu hết các bộ sưu tập Java chỉ sử dụng Object [].
josefx 23/2/2015

23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}

3
Tôi ủng hộ câu trả lời này vì đó là một giải pháp phù hợp với câu hỏi đang được hỏi. Tuy nhiên, đối với những người muốn điều hướng lên trên trong hệ thống phân cấp lớp như tôi với nhiều hơn một lớp Chung, điều này sẽ không hoạt động. Bởi vì bạn sẽ nhận được java.lang.object thay vì lớp thực tế.
KMC

3
Xin lưu ý rằng giải pháp này CHỈ hoạt động nếu lớp giữ loại chung là TÓM TẮT
Baba Mới

Không hoạt động với lớp trừu tượng của tôi trong Java 11
JRA_TLL

@JRA_TLL bạn rõ ràng đã làm điều gì đó sai. Tôi chỉ sử dụng nó với Java 12 và hoạt động như một bùa mê.
Googie

Nếu bạn muốn điều hướng lên trong cấu trúc phân cấp xem, bạn có thể truyền genericSuperclass thành Class <*> và lấy genericSuperclass. Tốt nhất là trong một vòng lặp.
fupduck

21

Tôi đã sử dụng theo cách tiếp cận:

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}

9
Tôi có "ngoại lệ trong chủ đề 'chính' java.lang.ClassCastException: java.lang.Class không thể được đúc để java.lang.reflect.ParameterizedType" với mẫu mã này
Yuyang

16

Tôi không nghĩ bạn có thể, Java sử dụng kiểu xóa khi biên dịch để mã của bạn tương thích với các ứng dụng và thư viện đã được tạo trước tiền chung.

Từ Tài liệu Oracle:

Loại tẩy

Generics đã được giới thiệu với ngôn ngữ Java để cung cấp các kiểm tra loại chặt chẽ hơn tại thời điểm biên dịch và để hỗ trợ lập trình chung. Để triển khai generic, trình biên dịch Java áp dụng kiểu xóa để:

Thay thế tất cả các tham số loại trong các loại chung bằng giới hạn hoặc Đối tượng của chúng nếu các tham số loại không bị ràng buộc. Do đó, mã byte được tạo ra chỉ chứa các lớp, giao diện và phương thức thông thường. Chèn phôi loại nếu cần thiết để bảo vệ an toàn loại. Tạo các phương thức cầu để bảo tồn tính đa hình trong các loại chung mở rộng. Kiểu xóa đảm bảo rằng không có lớp mới nào được tạo cho các kiểu tham số; do đó, generic không phát sinh thời gian chạy.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html


Jup, không thể nào. Java sẽ cần các tổng quát thống nhất để làm việc đó.
Henning

15

Kỹ thuật mô tả trong bài viết này của Ian Robertson làm việc cho tôi.

Trong ví dụ nhanh và bẩn:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }

3
Tại dòng cụ thể nào trong mã này là thực tế, các tham số loại thời gian chạy đang được đọc?
Ivan Matavulj

đây? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek

9

Tôi nghĩ rằng có một giải pháp thanh lịch khác.

Những gì bạn muốn làm là (một cách an toàn) "chuyển" loại tham số loại chung lên từ lớp concerete sang lớp cha.

Nếu bạn cho phép mình nghĩ loại lớp là "siêu dữ liệu" trên lớp, điều đó gợi ý phương thức Java để mã hóa siêu dữ liệu trong thời gian chạy: chú thích.

Đầu tiên xác định một chú thích tùy chỉnh dọc theo các dòng này:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

Sau đó, bạn có thể phải thêm chú thích vào lớp con của mình.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

Sau đó, bạn có thể sử dụng mã này để lấy loại lớp trong lớp cơ sở của mình:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

Một số hạn chế của phương pháp này là:

  1. Bạn chỉ định loại chung ( PassedGenericType) ở TWO thay vì loại không phải DRY.
  2. Điều này chỉ có thể nếu bạn có thể sửa đổi các lớp con cụ thể.

Vâng, nó không phải là DRY, tuy nhiên nó sạch hơn phương pháp mở rộng được đề xuất ở trên. Tôi thích nó. cảm ơn
agodinhost

8

Đây là giải pháp của tôi:

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}

10
Đây không phải là một câu trả lời cho câu hỏi này.
Adam Arold

1
Mã của tôi không phải là giải pháp chính xác cho câu hỏi. Nó trả về các tham số loại chung của lớp, nhưng không phải là loại thực tế của T. Nhưng nó có thể hữu ích cho những người khác vấp phải câu hỏi và đang tìm giải pháp của tôi.
Matthias M

1
getClass (). getGenericSuperclass () sẽ đạt được hiệu quả tương tự.
Gorky

5

Đây là giải pháp làm việc !!!

@SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    } 

GHI CHÚ: Chỉ có thể được sử dụng như siêu lớp
1. Phải được mở rộng với lớp đã nhập ( Child extends Generic<Integer>)
HOẶC

2. Phải được tạo dưới dạng triển khai ẩn danh ( new Generic<Integer>() {};)


4

Bạn không thể. Nếu bạn thêm một biến thành viên loại T vào lớp (bạn thậm chí không phải khởi tạo nó), bạn có thể sử dụng biến đó để khôi phục loại.


2
Rất tiếc, ok. Bạn làm phải khởi nó từ một constructor.
andrewmu

4

Đây là một cách mà tôi phải sử dụng một hoặc hai lần:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

Cùng với

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}

4
Điều này về mặt kỹ thuật hoạt động, tuy nhiên nó không giải quyết được trường hợp chung và tôi nghĩ đó là những gì các poster ban đầu là sau.
ggb667

Điều này không xứng đáng được bình chọn như đã có - poster ban đầu chưa rõ ràng. Câu trả lời này Mời một mẫu thiết kế mà làm việc và rất dễ dàng để thực hiện, miễn là nó phù hợp để làm cho lớp chung trừu tượng.
VirtualMichael

4

Một giải pháp đơn giản cho chiếc taxi này như dưới đây

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}

3

Để hoàn thành một số câu trả lời ở đây, tôi đã phải lấy ParametrienedType của MyGenericClass, bất kể thứ bậc cao đến đâu, với sự trợ giúp của đệ quy:

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}

1

Đây là mẹo của tôi:

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}

1
Lưu ý: điều này không hoạt động khi Tlà một biến loại. Trong trường hợp đó Tlà một biến kiểu, các varargs tạo ra một mảng xóa T. Xem ví dụ: http://ideone.com/DIPNwd .
Radiodef

1
Điều này trả về "Đối tượng"
yahya

Có thể bạn đang cố gắng trả lời một số câu hỏi khác
Khan

1

Chỉ trong trường hợp bạn sử dụng lưu trữ một biến bằng cách sử dụng loại chung, bạn có thể dễ dàng giải quyết vấn đề này bằng cách thêm phương thức getClassType như sau:

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

Tôi sử dụng đối tượng lớp được cung cấp sau để kiểm tra xem nó có phải là một thể hiện của một lớp nhất định không, như sau:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}

2
Đây là vấn đề, không may. Trước hết, nếu valuenullgì? Thứ hai, nếu valuelà một lớp con của T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();trả về Integer.classkhi nó phải trả lại Number.class. Nó sẽ đúng hơn để trở lại Class<? extends T>. Integer.class là một Class<? extends Number> nhưng không phải là a Class<Number>.
Radiodef

1

Đây là giải pháp của tôi

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

Cho dù hệ thống phân cấp lớp của bạn có bao nhiêu cấp, giải pháp này vẫn hoạt động, ví dụ:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

Trong trường hợp này, getMyType () = java.lang.String


Đây không phải là trả về loại T. Nó trả về T không phải java.lang. Ngoài ra, mã không chuyển đổi Loại thành Lớp <T>
Mohy Eldeen

Đây là một mẫu trực tuyến tôi đã thực hiện. Nhấp vào biên dịch và thực thi, sau đó bạn có thể nhận được kết quả. guidespoint.com/ từ
Lin Yu Cheng

Hoạt động với tôi - khi WildFly Weld CDI phá vỡ một phương pháp thay thế.
Andre

Tôi đã nhậnException in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
yuyang

0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

0

Đây là giải pháp của tôi. Các ví dụ nên giải thích nó. Yêu cầu duy nhất là một lớp con phải đặt loại chung, không phải là một đối tượng.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}

-1

Tôi đã làm giống như @Moesio ở trên nhưng trong Kotlin nó có thể được thực hiện theo cách này:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}

-4

Nó có thể hữu ích cho ai đó. Bạn có thể sử dụng java.lang.ref.WeakReference; cách này:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

Lớp này được sử dụng như thế nào? Tại sao WeakReference? Vui lòng cung cấp một số giải thích với câu trả lời của bạn, không chỉ một số mã.
Abhijit Sarkar

Vì vậy, nếu bạn có một SomeClass<MyClass> bạn có thể khởi tạo SomeClassvà gọi getTypevào trường hợp đó và có thời gian chạy MyClass.
TheLetch

Chắc chắn, nhưng tại sao WeakReference? Những gì bạn nói không khác với hầu hết các câu trả lời khác.
Abhijit Sarkar

Đầu tiên cách tiếp cận của tôi ngắn hơn (ít mã hơn), thứ hai, các tham chiếu Yếu không ngăn cản các tham chiếu của họ được hoàn thiện, và theo như tôi biết thì nó không sử dụng sự phản chiếu, do đó nhanh chóng
TheLetch

Điều này không có được loại bất cứ điều gì, điều này trả về một đối tượng của loại hình đó, trong đó, FYI, bạn có thể làm với nghĩa đen bất kỳ loại wrapper ( AtomicReference, List, Set).
Mặt trận

-5

Tôi thấy đây là một giải pháp đơn giản dễ hiểu và dễ giải thích

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}

Giải thích (T...t). (Đó là lý do tại sao mã này không hoạt động.)
Christopher Schneider
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.