简体   繁体   English

当类中没有定义泛型类型变量时,处理泛型类成员

[英]Handling generic class members when there is no generic type variable defined in class

Please have a look at the following code: 请看下面的代码:

public class MyClass<T extends MyObject> {
    private Class<T> clazz;
    public void setClass1(Class<T> clazz) {
        this.clazz = clazz;
    }
    public Class<T> getClass1() {
        return clazz;
    }
    public T getObject1() {
        T o = null;
        try {
            o = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return o;
    }
}

The above works great but requires the class type T to be known before any MyClass object is created. 上面的工作很好但是在创建任何MyClass对象之前需要知道类类型T Now if I want to delay specifying T until setClass1() is ever called, I have to remove <T extends MyObject> from MyClass declaration to something like below: 现在,如果我想延迟指定T直到调用setClass1() ,我必须从MyClass声明中删除<T extends MyObject> ,如下所示:

public class MyClass {
//  private Class<T> clazz; // doesn't work anymore as T is unknown, so do the following instead
    private Class<? extends MyObject> clazz;
    public <T extends MyObject> void setClass1(Class<T> clazz) {
        this.clazz = clazz; // OK
    }
    public void setClass2(Class<? extends MyObject> clazz) {
        this.clazz = clazz; // OK
    }
    public <T extends MyObject> Class<T> getClass1() {
        return clazz; // Error, need casting to Class<T>
    }
    public Class<? extends MyObject> getClass2() {
        return clazz; // OK
    }
    public <T extends MyObject> T getObject1() {
        T o = null;
        try {
            o = clazz.newInstance(); // Error, must cast to T
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return o;
    }
    public MyObject getObject2() {
        MyObject o = null;
        try {
            o = clazz.asSubclass(MyObject.class).newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return o; // OK, but lost the difference between ? extends MyObject and MyObject
    }
}

First, why getClass1() gives error, while setClass1() is OK? 首先,为什么getClass1()给出错误,而setClass1()是正常的?

Second, why the return in getObject1() must be cast to (T) ? 第二,为什么getObject1()的返回必须强制转换为(T)

Third, how do you fix getObject1() without unnecessary casting nor @SuppressWarnings("unchecked") ? 第三,如何在没有不必要的转换或@SuppressWarnings("unchecked")情况下修复getObject1() @SuppressWarnings("unchecked")

Fourth, in my case would changing from using setClass1() , getClass1() , getObject1() to setClass2() , getClass2() , getObject2() better? 第四,在我的情况下会改变从使用setClass1()getClass1()getObject1()setClass2()getClass2()getObject2()更好吗?

First, why getClass1() gives error, while setClass1() is OK? 首先,为什么getClass1()给出错误,而setClass1()是正常的?

In setClass() you assign a Class<? extends MyObject> setClass()你分配一个Class<? extends MyObject> Class<? extends MyObject> to a variable of type Class<? extends MyObject> Class<? extends MyObject>Class<? extends MyObject>类型的变量 Class<? extends MyObject> . Class<? extends MyObject> That is allowed. 这是允许的。

In getClass() , you want to retrieve a Class<T> from a variable of type Class<? extends MyObject> getClass() ,您想要从Class<? extends MyObject>的类型的变量中检索Class<T> Class<? extends MyObject> Class<? extends MyObject> . Class<? extends MyObject> But the compiler has no way to know if what is stored in the variable is really a Class<T> or perhaps a Class<U> . 但是编译器无法知道变量中存储的内容是否真的是Class<T>或者Class<U> So this is forbidden. 所以这是禁止的。

Second, why the return in getObject1() must be cast to (T)? 第二,为什么getObject1()中的返回必须强制转换为(T)?

Class<? extends MyObject>.newInstance() Class<? extends MyObject>.newInstance() returns an object of type ? extends MyObject Class<? extends MyObject>.newInstance()返回一个类型为? extends MyObject的对象 ? extends MyObject . ? extends MyObject This is not the same as T . 这与T That's why you have to cast. 这就是你必须施展的原因。

Third, how do you fix getObject1() without unnecessary casting nor @SuppressWarnings("unchecked")? 第三,如何在没有不必要的转换或@SuppressWarnings(“未选中”)的情况下修复getObject1()?

As explained, the version above does not contain an unnecessary cast. 如上所述,上面的版本不包含不必要的强制转换。 You either have to make T a generic parameter of the class itself or you need the cast. 您要么必须使T成为类本身的通用参数,要么需要强制转换。

private Class<? extends MyObject> clazz;

public <T extends MyObject> void setClass1(Class<T> clazz) {
    this.clazz = clazz; // OK
}

In this example, clazz is defined as a reference that can hold any kind of Class objects that are of some unknown type that is a subclass of MyObject . 在此示例中, clazz被定义为可以包含任何类型的Class对象的引用,这些类对象是MyObject的子类。

The any part is important. 任何部分都很重要。 This is why setClass1 works since it assigns effectively the same thing to clazz - a subtype of MyObject . 这就是setClass1工作的原因,因为它有效地为clazz分配了同样的东西 - 一个MyObject的子类型。 Because of this, the compiler will not complain. 因此,编译器不会抱怨。

Your method setClass1 is effectively a more generic version of this method: 您的方法setClass1实际上是此方法的更通用版本:

public void setClass3(Class<MyObject> clazz) {
    this.clazz = clazz; // OK
}

since setClass3 assigns the most concrete type Class<MyObject> to clazz , which can hold any Class that is MyObject or a subclass of it. 因为setClass3将最具体的类型Class<MyObject>分配给clazz ,它可以包含任何 MyObject或其子类。

Moving on. 继续。

  public <T extends MyObject> Class<T> getClass1() {
        return clazz; // Error, need casting to Class<T>
  }

This will not work since T is a subclass of MyObject , but can you be certain that clazz holds Class<MyObject> or Class<T> for some value of T? 这不起作用,因为TMyObject的子类,但是你可以确定clazzClass<T>的某个值保存Class<MyObject>Class<T>吗?

For example, imagine you also have these classes defined somewhere: 例如,假设您还在某处定义了这些类:

public class MySubObject1 extends MyObject {...}

and

public class MySubObject2 extends MyObject {...}

Imagine that the wildcard type ? 想象一下通配符类型? represents MySubObject1 while T represents MySubObject2 and since they bear no relationship to each other, the compiler will complain. 代表MySubObject1T代表MySubObject2 ,由于它们彼此没有关系,编译器会抱怨。 Remember, you cannot do this: 请记住,你不能这样做:

    MySubObject1 so1 = new MySubObject2(); // compiler error

    MySubObject2 so2 = new MySubObject1(); // compiler error

A similar problem exists in getObject1 - the compiler cannot know that T represents a class or a superclass of clazz (remember it can hold any subclass of MyObject ), that's why the casting is necessary. getObject1存在类似的问题 - 编译器无法知道T表示clazz的类或超类(记住它可以包含MyObject任何子类),这就是为什么需要进行转换的原因。

With your current design, you cannot really avoid casting and @SuppressWarnings("unchecked") in getObject1 . 使用当前的设计,您无法真正避免在getObject1转换和@SuppressWarnings("unchecked") If possible, try to avoid mixing generic types and wildcards. 如果可能,请尽量避免混合泛型类型和通配符。

One more thing. 还有一件事。 getClass2 forces its users to deal with a wildcard - not a great API design decision. getClass2强制其用户处理通配符 - 这不是一个很好的API设计决策。

Methods in your public class MyClass class have potential type safety issues. public class MyClass方法public class MyClass类具有潜在的类型安全问题。

Are you sure that your original public class MyClass<T extends MyObject> class doesn't meet your needs? 您确定原始public class MyClass<T extends MyObject> class不符合您的需求吗? It looks fine to me, except the clazz.newInstance(); 它看起来很好,除了clazz.newInstance(); part since it requires MyObject and every one of its subclasses to have a zero argument constructor. 因为它需要MyObject和它的每个子类都有一个零参数构造函数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM