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


247

这个问题上,对泛型及其背后的作用进行了很好的讨论,因此我们都知道泛型Vector<int[]>是整数数组的向量,并且HashTable<String, Person>是一个表,其键是字符串和值Persons。但是,让我感到困扰的是的用法Class<>

Java类Class应该也采用一个模板名称,(否则,Eclipse中的黄色下划线告诉我)。我不明白该放什么。整个Class对象就是当您不完全了解有关对象的信息(例如反射)时。为什么要让我指定Class对象将容纳哪个类?我显然不知道,或者我不会使用该Class对象,而是使用特定的对象。

Answers:


136

使用类的通用版本,Class可以让您编写诸如

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

然后您可以确定收到的Class对象可以扩展Collection,并且该类的实例将(至少)是Collection。


183

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

例如)

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

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

现在假设

Teacher t = new Teacher();

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

Class studentClassRef = t.getClass();

但这是错误的..?

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

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

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

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

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

因此Class<T>代表特定类类型的类对象'T '。

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

/**
 * 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; }
}

在这里T可以String用作CarName的类型

OR T可以用作modelNumber的Integer类型,

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

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

通用方法

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

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

 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;
}

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

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

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

但是下面是错误的,因为类级别的类型参数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());
    }
}

其他有效场景

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;
        }
    }

最后,静态方法始终需要显式<T>声明;它不会从课堂上衍生出来Class<T>。这是因为类级别T与实例绑定。

另请参阅泛型限制

通配符和子类型

通用方法的类型参数



“ Class <T>代表特定类类型'T'的类对象。”确实是有道理的。谢谢..
bynu022

这个答案在使用Class(关于Java)的问题中使用Class(来自学校)的方式非常令人困惑。很难知道作者从一个句子到另一个句子在说什么。
Echox

@Echox对不起,如果您有任何具体问题,我可以改善。
卡纳加维卢·苏古玛


32

从Java文档中:

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

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

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

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

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

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


2
我(认为我)做到了。我使用过的(各种)框架要求您传递模块所依赖的服务的类名。我在使用Class对象的基础上构建了一层,以限制选择的数量。使用Class<? extends X>我认为的表示法,我可以将其限制为仅“服务”类型。除非没有通用的“服务”类型,所以我只能使用Class<?>。唉。
fwielstra 2011年

9

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

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

5

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


@SuppressWarnings("unchecked")来救援!(只是要小心,始终把它应用到尽可能小的范围内尽可能它不会在你的代码晦涩的潜在问题。)
多纳尔研究员

4

继@Kire Haglin的答案之后,可以在JAXB解组文档中看到泛型方法的另一个示例:

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();
}

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


2

您通常想将通配符与一起使用Class。例如,Class<? extends JComponent>可以允许您指定该类是的某个子类JComponent。如果您已Class从中检索到实例Class.forName,则可以Class.asSubclass在尝试构建实例之前使用进行强制类型转换。


2

在Java中<T>表示泛型类。通用类是可以处理任何类型的数据类型的类,换句话说,我们可以说它是与数据类型无关的。

public class Shape<T> {
    // T stands for "Type"
    private T t;

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

其中T表示类型。现在,当您创建此Shape类的实例时,您将需要告诉编译器它将处理哪种数据类型。

例:

Shape<Integer> s1 = new Shape();
Shape<String> s2 = new Shape();

整数是一个类型,字符串也是一个类型。

<T>专门代表泛型。根据Java Docs-泛型类型是通过类型进行参数化的泛型类或接口。


0

仅举另一个例子,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();
                }
            });
}

-2

一开始令人困惑。但这在以下情况下有帮助:

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.

4
这不是说Action a = new Action()的一种非常复杂的方式吗?
卡尔2010年

1
Action a = new Action() Action我是一个接口,这是SomeAction我们正在尝试获取实例。我们只有SomeAction在运行时可用的名称。
fastcodejava

这不会通过类型检查:Java编译器无法告诉<code> Class.forName(“ SomeAction”)</ code>类型为<code> Class <Action> </ code>,因为这只会在运行时被知道。
tonio

@tonio,对,所以您可能必须以某种try / catch方式包装第一行。但是,假设没有异常抛出,那么第二行就可以保证正常工作。
MatrixFrog 2011年

2
您真正描述的是SomeAction.class与模式Class <?相匹配。扩展Action>-也就是说,如果您有方法useAction(Class <?extended Action> klass),则可以调用useAction(SomeAction.class)。
托马斯·安德鲁斯

-5

只需使用牛肉课:

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();
}

4
这实际上并不能提供问题的完整答案。如果您认为需要添加一些内容,请编辑此答案。
MasterAM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.