【问题标题】:migrating java classes to use generics [duplicate]迁移java类以使用泛型[重复]
【发布时间】:2015-06-16 06:16:14
【问题描述】:

我在我的一堂课中使用泛型时遇到问题。这些是我的课程:

public interface InterE<PK> {}

public interface Inter2<O extends ClassO, P extends ClassP> {}

public class ClassU<O extends ClassO, P extends ClassP> implements InterE<Long> {}

public class ClassP {}

public class ClassO {}

public class Class1<PK, T extends InterE<PK>> {
    public Class1 (Class<T> clazz) {}
}

public class Class2<O extends ClassO, P extends ClassP> extends Class1<Long, ClassU<O, P>> implements Inter2<O, P> {
    public Class2 () {
        //old constructor
        //super(ClassU.class);
        //new - here is problem
        super(ClassU<O, P>.class);
    }
}

如果 ClassU 不使用泛型,那么 Class2 中的旧构造函数可以正常工作,但我需要在 ClassU 中使用泛型,所以我在 Class2 中调用超级构造函数有问题。

【问题讨论】:

  • 以“旧”方式使用构造函数时究竟会发生什么?
  • @serg.nechaev 不完全是,他不是在寻找 O.classP.class
  • 旧方式有编译错误 - 构造函数 Class1>(Class) 未定义
  • 可能没有安全的方法绕过它,但您可以使用不安全的双重强制转换:super((Class&lt;ClassU&lt;O, P&gt;&gt;)(Class)ClassU.class)。见这里stackoverflow.com/questions/7502243/…

标签: java generics


【解决方案1】:

简短的故事:Class1构造函数参数类型从Class&lt;T&gt;更改为Class&lt;?&gt;

为什么?

Class1 只需要T 扩展InterEClass2 声明 ClassU 是这个 T 参数。由于ClassU 实现了InterE,所以Class1 一切正常。

你真的需要Class1 构造函数中的&lt;T&gt; 信息吗?我认为没有:
1) T 已经存在于类声明中:Class1&lt;PK, T extends InterE&lt;PK&gt;&gt;,您可以在类中使用它来生成代码并使其在编译时安全:

 public class Class1<PK, T extends InterE<PK>> {
     Map<PK, T> map = new HashMap<>(); // can still use T, compile-time safe
 }

2) Class&lt;T&gt; 能做什么,Class&lt;?&gt; 不能做什么? - 它可以创建newInstance() 安全并做更多的事情,但你很可能不会使用这种可能性。使用时还可以使用T,例如调用T instance = (T) class.newInstance();,这样就不会有任何不可逾越的限制。

【讨论】:

  • 是的,你是对的。在 Class1 中没有什么特别的。它主要用于检查以避免在运行时对构造函数和异常的错误输入
  • @podmak 如果答案解决了您的问题,您应该将其标记为已接受,以便其他用户可以使用它。否则,写下为什么它不能解决问题或编辑原始问题。
  • 你不应该使用原始类型,见my comment on podmaks answer
  • @siegi 好的,我改了
  • @AdamSkywalker 是绝对正确的,T 已经在 Class1 上可用以添加类型安全并且 Class2 扩展了 Class1,因此要求传入它是多余的
【解决方案2】:

从您的示例中不清楚为什么需要保留参数;一旦您了解了type erasure 以及泛型如何是编译时检查而不是运行时检查,则并非所有泛型都一定有意义。

如果您确实想在运行时保留和操作参数化类型,Google's Guava 库提供TypeToken,例如

public class Class1<PK, T extends InterE<PK>> {
    public Class1 (TypeToken<T> token) {}
}

public class Class2<O extends ClassO, P extends ClassP> extends Class1<Long, ClassU<O, P>> implements Inter2<O, P> {
    public Class2 () {
        super(new TypeToken<ClassU<O, P>>() {});
    }
}

【讨论】:

    【解决方案3】:

    dkhe 的回答是正确的: super((Class&lt;ClassU&lt;O, P&gt;&gt;) (Class) ClassU.class)

    也为我工作: super((Class) ClassU.class)

    【讨论】:

    • 您不应该使用没有类型参数的Class(称为原始类型)。来自Java Language Specification, §4.8只允许使用原始类型作为对遗留代码兼容性的让步。强烈建议不要使用原始类型 […]。未来的版本 [...] 可能会禁止使用原始类型。 最好将 Class&lt;?&gt; 用作 Damsen shows in his answer
    • 转换为 Class&lt;?&gt; 导致编译错误 - 未定义的构造函数
    • 所有的转换都向后弯曲,以通过转换使编译器满意,这不是类型安全的。这违背了首先尝试使用泛型的意义——在编译时提供类型安全——因为类型在运行时被擦除。
    • @podmak:当(Class) 被替换为(Class&lt;?&gt;) 时,第一种形式应该可以工作。第二种形式完全禁用该语句的泛型,因为您使用的是 原始类型。我猜你在将&lt;?&gt; 添加到第二种形式时遇到了编译错误(预计不起作用)......
    • @Danikov:正如我已经说过的in this comment:我同意强制转换会破坏类型安全的目的在大多数情况下。但是,您能否向我展示一个在上述情况下通过转换 Class 实例引起的问题的示例?
    【解决方案4】:

    试试这个:

    super((Class<ClassU<O, P>>)(Class<?>)ClassU.class);
    

    参考:Class object of generic class (java)

    【讨论】:

    • Wanton 强制转换不是类型安全的,这违背了首先拥有泛型的意义。
    • @Danikov:我同意这种类型的转换违背了泛型的目的在大多数情况下,但Class 是一种特殊情况,因为您无法获得Class 对象具有特定的泛型类型。
    • @siegi 很容易得到Class&lt;String&gt;。问题类似于Class&lt;Map&lt;?,?&gt;&gt;
    • @Danikov:是的,抱歉,我并不清楚。但重点仍然存在:如果不进行强制转换,您应该如何获得 Class&lt;Map&lt;?,?&gt;&gt;Class&lt;ClassU&lt;O,P&gt;&gt;
    • @siegi 你不能,但更重要的是,它没有意义。请记住,类型在运行时被擦除并且仅在编译时检查,而且泛型仅通过实例解析。当您询问对象的 Class 时,这是一个静态属性。该信息不存在于该范围内。这就是为什么 Guava 的 TypeToken 在编译和运行时都可以工作的原因,因为它是匿名实例化的。
    【解决方案5】:

    最简单的方法是:

    public class Class1<PK, T extends InterE<PK>> {
         public Class1 (Class<? extends T> clazz) {}
    }
    
    
    public class Class2<O extends ClassO, P extends ClassP> extends Class1<Long, ClassU<O, P>> implements Inter2<O, P> {
        public Class2 () {
           super((Class<? extends ClassU<O, P>>) ClassU.class);
        }}
    

    编辑

    适用于 Java 1.6

    【讨论】:

    • 这给了我与 OP 代码相同的错误。它不会也给你一个错误吗?
    • 对不起,@DanGetz,你是对的。构造函数应该是:super((Class&lt;? extends ClassU&lt;O, P&gt;&gt;) ClassU.class);
    • 我仍然收到错误消息。
    • 我在 java 1.6 上测试了这个。你用的是哪个版本的java?
    • 1.7,也许这就是区别。
    猜你喜欢
    • 2012-03-13
    • 2011-08-17
    • 1970-01-01
    • 2017-11-06
    • 2014-06-12
    • 2018-06-12
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多