如何在 Java 中使用 Class<T>?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/462297/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 14:55:41  来源:igfitidea点击:

How to use Class<T> in Java?

javatemplatesclassgenerics

提问by Karl

There's a good discussion of Generics and what they really do behind the scenes over at this question, so we all know that Vector<int[]>is a vector of integer arrays, and HashTable<String, Person>is a table of whose keys are strings and values Persons. However, what stumps me is the usage of Class<>.

这个问题上对泛型以及它们在幕后真正做了什么进行了很好的讨论,所以我们都知道这Vector<int[]>是一个整数数组的向量,并且HashTable<String, Person>是一个键是字符串和值Person的表。然而,让我难受的是Class<>.

The java class Classis supposed to also take a template name, (or so I'm being told by the yellow underline in eclipse). I don't understand what I should put in there. The whole point of the Classobject is when you don't fully have the information about an object, for reflection and such. Why does it make me specify which class the Classobject will hold? I clearly don't know, or I wouldn't be using the Classobject, I would use the specific one.

java 类Class也应该采用模板名称,(或者我被 eclipse 中的黄色下划线告知)。我不明白我应该在那里放什么。Class对象的全部意义在于当您没有完全了解对象的信息时,例如反射等。为什么它让我指定Class对象将包含哪个类?我显然不知道,或者我不会使用该Class对象,我会使用特定的对象。

采纳答案by Yuval

Using the generified version of class Class allows you, among other things, to write things like

使用类 Class 的泛型版本,除其他外,您还可以编写诸如

Class<? extends Collection> someCollectionClass = someMethod();

and then you can be sure that the Class object you receive extends Collection, and an instance of this class will be (at least) a Collection.

然后你可以确定你收到的 Class 对象扩展了Collection,这个类的一个实例(至少)是一个集合。

回答by raupach

From the Java Documentation:

从 Java 文档:

[...] More surprisingly, class Class has been generified. Class literals now function as type tokens, providing both run-time and compile-time type information. This enables a style of static factories exemplified by the getAnnotation method in the new AnnotatedElement interface:

[...] 更令人惊讶的是,Class Class 被泛化了。类文字现在用作类型标记,提供运行时和编译时类型信息。这启用了一种静态工厂样式,例如新 AnnotatedElement 接口中的 getAnnotation 方法:

<T extends Annotation> T getAnnotation(Class<T> annotationType); 

This is a generic method. It infers the value of its type parameter T from its argument, and returns an appropriate instance of T, as illustrated by the following snippet:

这是一个通用的方法。它从其参数推断其类型参数 T 的值,并返回 T 的适当实例,如以下代码段所示:

Author a = Othello.class.getAnnotation(Author.class);

Prior to generics, you would have had to cast the result to Author. Also you would have had no way to make the compiler check that the actual parameter represented a subclass of Annotation. [...]

在使用泛型之前,您必须将结果强制转换为 Author。此外,您将无法让编译器检查实际参数是否代表 Annotation 的子类。[...]

Well, I never had to use this kind of stuff. Anyone?

嗯,我从来没有用过这种东西。任何人?

回答by Tom Hawtin - tackline

You often want to use wildcards with Class. For instance, Class<? extends JComponent>, would allow you to specify that the class is some subclass of JComponent. If you've retrieved the Classinstance from Class.forName, then you can use Class.asSubclassto do the cast before attempting to, say, construct an instance.

您经常希望将通配符与Class. 例如,Class<? extends JComponent>, 将允许您指定该类是 的某个子类JComponent。如果您已经Class从 中检索了实例Class.forName,那么您可以Class.asSubclass在尝试构建实例之前使用来进行转换。

回答by bruno conde

As other answers point out, there are many and good reasons why this classwas made generic. However there are plenty of times that you don't have any way of knowing the generic type to use with Class<T>. In these cases, you can simply ignore the yellow eclipse warnings or you can use Class<?>... That's how I do it ;)

正如其他答案所指出的那样,class将其设为通用有很多很好的理由。但是,很多时候您无法知道要使用的泛型类型Class<T>。在这些情况下,您可以简单地忽略黄色日食警告,或者您可以使用Class<?>...我就是这样做的;)

回答by Kire Haglin

I have found class<T>useful when I create service registry lookups. E.g.

我发现class<T>在创建服务注册表查找时很有用。例如

<T> T getService(Class<T> serviceClass)
{
    ...
}

回答by fastcodejava

It is confusing in the beginning. But it helps in the situations below :

一开始很混乱。但它有助于以下情况:

class SomeAction implements Action {
}

// Later in the code.
Class<Action> actionClass = Class.forName("SomeAction"); 
Action action = actionClass.newInstance();
// Notice you get an Action instance, there was no need to cast.

回答by Stew

Following on @Kire Haglin's answer, a further example of generics methods can be seen in the documentation for JAXB unmarshalling:

在@Kire Haglin 的回答之后,可以在JAXB unmarshalling文档中看到泛型方法的进一步示例:

public <T> T unmarshal( Class<T> docClass, InputStream inputStream )
         throws JAXBException {
  String packageName = docClass.getPackage().getName();
  JAXBContext jc = JAXBContext.newInstance( packageName );
  Unmarshaller u = jc.createUnmarshaller();
  JAXBElement<T> doc = (JAXBElement<T>)u.unmarshal( inputStream );
  return doc.getValue();
}

This allows unmarshalto return a document of an arbitrary JAXB content tree type.

这允许unmarshal返回任意 JAXB 内容树类型的文档。

回答by yaa

Just use the beef class:

只需使用牛肉类:

public <T> T beefmarshal( Class<beef> beefClass, InputBeef inputBeef )
     throws JAXBException {
     String packageName = docClass.getPackage().getBeef();
     JAXBContext beef = JAXBContext.newInstance( packageName );
     Unmarshaller u = beef.createBeef();
     JAXBElement<T> doc = (JAXBElement<T>)u.beefmarshal( inputBeef );
     return doc.getBeef();
}

回答by Kanagavelu Sugumar

All we know is "All instances of a any class shares the same java.lang.Class object of that type of class"

我们所知道的是“任何类的所有实例共享该类型类的相同 java.lang.Class 对象

e.g)

例如)

Student a = new Student();
Student b = new Student();

Then a.getClass() == b.getClass()is true.

然后a.getClass() == b.getClass()是真的。

Now assume

现在假设

Teacher t = new Teacher();

without generics the below is possible.

没有泛型,下面是可能的。

Class studentClassRef = t.getClass();

But this is wrong now ..?

但现在这是错误的..?

e.g) public void printStudentClassInfo(Class studentClassRef) {}can be called with Teacher.class

例如)public void printStudentClassInfo(Class studentClassRef) {}可以用Teacher.class

This can be avoided using generics.

使用泛型可以避免这种情况。

Class<Student> studentClassRef = t.getClass(); //Compilation error.

Now what is T ?? T is type parameters (also called type variables); delimited by angle brackets (<>), follows the class name.
T is just a symbol, like a variable name (can be any name) declared during writing of the class file. Later that T will be substituted with
valid Class name during initialization (HashMap<String> map = new HashMap<String>();)

现在什么是T??T是类型参数(也叫类型变量);由尖括号 (<>) 分隔,跟在类名之后。
T 只是一个符号,就像在编写类文件时声明的变量名(可以是任何名称)。稍后 T 将
在初始化期间用有效的类名替换( HashMap<String> map = new HashMap<String>();)

e.g) class name<T1, T2, ..., Tn>

例如) class name<T1, T2, ..., Tn>

So Class<T>represents a class object of specific class type 'T'.

SoClass<T>表示特定类类型“ T”的类对象。

Assume that your class methods has to work with unknown type parameters like below

假设您的类方法必须使用未知类型参数,如下所示

/**
 * Generic version of the Car class.
 * @param <T> the type of the value
 */
public class Car<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

Here T can be used as Stringtype as CarName

这里 T 可以String用作CarName类型

OR T can be used as Integertype as modelNumber,

OR T 可以用作modelNumberInteger类型,

OR T can be used as Objecttype as valid car instance.

OR T 可以用作有效汽车实例的Object类型。

Now here the above is the simple POJO which can be used differently at runtime.
Collections e.g) List, Set, Hashmap are best examples which will work with different objects as per the declaration of T, but once we declared T as String
e.g) HashMap<String> map = new HashMap<String>();Then it will only accept String Class instance objects.

现在上面是简单的 POJO,它可以在运行时以不同的方式使用。
集合(例如)List、Set、Hashmap 是最好的例子,它们可以根据 T 的声明处理不同的对象,但是一旦我们将 T 声明为 String,
例如)HashMap<String> map = new HashMap<String>();那么它将只接受 String 类实例对象。

Generic Methods

通用方法

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

泛型方法是引入自己的类型参数的方法。这类似于声明泛型类型,但类型参数的范围仅限于声明它的方法。允许使用静态和非静态泛型方法,以及泛型类构造函数。

The syntax for a generic method includes a type parameter, inside angle brackets, and appears before the method's return type. For generic methods, the type parameter section must appear before the method's return type.

泛型方法的语法包括类型参数、尖括号内,并出现在方法的返回类型之前。对于泛型方法,类型参数部分必须出现在方法的返回类型之前。

 class Util {
    // Generic static method
    public static <K, V, Z, Y> boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

 class Pair<K, V> {

    private K key;
    private V value;
}

Here <K, V, Z, Y>is the declaration of types used in the method arguments which should before the return type which is booleanhere.

<K, V, Z, Y>是方法参数中使用的类型声明,它应该在boolean此处的返回类型之前。

In the below; type declaration <T>is not required at method level, since it is already declared at class level.

在下面; <T>方法级别不需要类型声明,因为它已经在类级别声明了。

class MyClass<T> {
   private  T myMethod(T a){
       return  a;
   }
}

But below is wrong as class-level type parameters K, V, Z, and Y cannot be used in a static context (static method here).

但是下面是错误的,因为类级别的类型参数 K、V、Z 和 Y 不能在静态上下文中使用(这里是静态方法)。

class Util <K, V, Z, Y>{
    // Generic static method
    public static  boolean compare(Pair<K, V> p1, Pair<Z, Y> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}

OTHER VALID SCENARIOS ARE

其他有效场景是

class MyClass<T> {

        //Type declaration <T> already done at class level
        private  T myMethod(T a){
            return  a;
        }

        //<T> is overriding the T declared at Class level;
        //So There is no ClassCastException though a is not the type of T declared at MyClass<T>. 
        private <T> T myMethod1(Object a){
                return (T) a;
        }

        //Runtime ClassCastException will be thrown if a is not the type T (MyClass<T>).  
        private T myMethod1(Object a){
                return (T) a;
        }       

        // No ClassCastException        
        // MyClass<String> obj= new MyClass<String>();
        // obj.myMethod2(Integer.valueOf("1"));
        // Since type T is redefined at this method level.
        private <T> T myMethod2(T a){
            return  a;
        }

        // No ClassCastException for the below
        // MyClass<String> o= new MyClass<String>();
        // o.myMethod3(Integer.valueOf("1").getClass())
        // Since <T> is undefined within this method; 
        // And MyClass<T> don't have impact here
        private <T> T myMethod3(Class a){
            return (T) a;
        }

        // ClassCastException for o.myMethod3(Integer.valueOf("1").getClass())
        // Should be o.myMethod3(String.valueOf("1").getClass())
    private  T myMethod3(Class a){
        return (T) a;
    }


        // Class<T> a :: a is Class object of type T
        //<T> is overriding of class level type declaration; 
        private <T> Class<T> myMethod4(Class<T> a){
            return  a;
        }
    }

And finally Static method always needs explicit <T>declaration; It wont derive from class level Class<T>. This is because of Class level T is bound with instance.

最后静态方法总是需要显式<T>声明;它不会从类级别派生Class<T>。这是因为类级别 T 与实例绑定。

Also read Restrictions on Generics

另请阅读对泛型的限制

Wildcards and Subtyping

通配符和子类型

type argument for a generic method

泛型方法的类型参数

回答by zeronone

Just to throw in another example, the generic version of Class (Class<T>) allows one to write generic functions such as the one below.

再举一个例子, Class ( Class<T>)的泛型版本允许编写泛型函数,例如下面的函数。

public static <T extends Enum<T>>Optional<T> optionalFromString(
        @NotNull Class<T> clazz,
        String name
) {
    return Optional<T> opt = Optional.ofNullable(name)
            .map(String::trim)
            .filter(StringUtils::isNotBlank)
            .map(String::toUpperCase)
            .flatMap(n -> {
                try {
                    return Optional.of(Enum.valueOf(clazz, n));
                } catch (Exception e) {
                    return Optional.empty();
                }
            });
}