首页 文章

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

提问于
浏览
212

有很多关于泛型的讨论以及他们在this question后面真实地做了什么,所以我们都知道 Vector<int[]> 是整数数组的向量, HashTable<String, Person> 是一个表,其键是字符串和值 Person . 然而,让我感到困惑的是 Class<> 的用法 .

java类 Class 也应该采用模板名称,(或者我理解我应该放在那里的东西. Class 对象的全部意义在于你没有完全掌握有关对象的信息,用于反射为什么它让我指定 Class 对象将保持哪个类?我显然没有 Class 使用 Class 对象,我会使用特定的对象 .

10 回答

  • 117

    使用类的类的通用版本允许您(除其他外)编写类似的东西

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

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

  • 0

    我们所知道的是“任何类的所有实例都共享该类类型的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只是一个符号,就像在编写类文件时声明的变量名(可以是任何名称) . 后来T将被替换
    初始化期间的有效类名称( HashMap<String> map = new HashMap<String>();

    例如) 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可以用作 Integer 类型 modelNumber

    OR T可以用作 Object 类型 valid car instance .

    现在,上面是简单的POJO,它可以在运行时以不同的方式使用 .
    集合例如List,Set,Hashmap是根据T的声明使用不同对象的最佳示例,但是一旦我们将T声明为String
    例如 HashMap<String> map = new HashMap<String>(); 然后它只接受String Class实例对象 .

    Generic Methods

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

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

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

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

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

    另请阅读Restrictions on Generics

  • -2

    从Java文档:

    [...]更令人惊讶的是, class 已经普及 . 类文本现在用作类型标记,提供运行时和编译时类型信息 . 这样可以在新的AnnotatedElement接口中使用getAnnotation方法示例的静态工厂样式:

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

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

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

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

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

  • 30

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

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

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

  • -5

    继@Kire Haglin的回答之后,_2485554中可以看到另一个泛型方法示例:

    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内容树类型的文档 .

  • 152

    您经常要使用带有 Class 的通配符 . 例如, Class<? extends JComponent> 将允许您指定该类是 JComponent 的某个子类 . 如果您从 Class.forName 检索了 Class 实例,则可以在尝试构建实例之前使用 Class.asSubclass 进行强制转换 .

  • 5

    换句话说,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();
                    }
                });
    }
    
  • 3

    一开始很困惑 . 但它有助于以下情况:

    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.
    
  • 2

    只需使用牛肉类:

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

相关问题