【问题标题】:Java Generics: casting to ? (or a way to use an arbitrary Foo<?>)Java 泛型:转换为 ? (或使用任意 Foo<?> 的方法)
【发布时间】:2009-09-03 20:20:42
【问题描述】:

所以,我有一些代码看起来差不多(为简洁起见截断 - 忽略诸如公共成员变量之类的东西):

  public class GenericThingy<T> {
    private T mValue;
    public final T[] mCandidates;

    public GenericThingy(T[] pCandidates, T pInitValue) {
      mCandidates = pCandidates;
      mValue = pInitValue;
    }

    public void setValue(T pNewValue) {
      mValue = pNewValue;
    }
  }

  public class GenericThingyWidget {

    private final GenericThingy<?> mThingy;
    private final JComboBox mBox;

    public GenericThingyWidget (GenericThingy<?> pThingy) {
      mThingy = pThingy;
      mBox = new JComboBox(pThingy.mCandidates);
      //do stuff here that makes the box show up
    }

    //this gets called by an external event
    public void applySelectedValue () {
      mThingy.setValue(mBox.getSelectedItem());
    }
  }
}

我的问题是 mThingy.setValue(mBox.getSelectedItem());调用会产生以下错误:

Generics.GenericThingy&lt;capture#4-of ?&gt; 类型中的方法setValue(capture#4-of ?) 不适用于参数(对象)

我可以通过从 GenericThingyWidget 中 mThingy 和 pThingy 的声明中删除 &lt;?&gt; 来解决这个问题 - 这给了我一个“GenericThingy 是原始类型。应该参数化对 GenericThingy 的引用”警告。

我也尝试将 setValue 调用替换为

mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);

我真的希望它可以工作,但这产生了一个非常相似的错误:

Generics.GenericThingy&lt;capture#4-of ?&gt; 类型中的方法setValue(capture#4-of ?) 不适用于参数(capture#5-of ?)

有没有办法做到这一点 生成“原始类型”警告(“未经检查的强制转换”警告我可以接受)并且不使 GenericThingyWidget 成为泛型类型?我想我可以将 mBox.getSelectedItem() 的返回值转换为某些东西,但我不知道那会是什么。

作为附加问题,为什么对 mThingy.setValue 的替换调用不起作用?

【问题讨论】:

    标签: java generics casting


    【解决方案1】:

    您缺少GenericThingyWidget 中的信息。 ? 你放的意思是:任何类扩展对象。这意味着任何,而不是某个特定的,但我不知道是哪个。 Java 不能将一个? 与另一个相关联,它们不能在类层次结构树中相互关联。所以

    mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
    

    这试图将一个any类的对象放入setValue中,它正在等待any其他类,但是?不能告诉Java这两个any 应该是同一个类。

    如果没有参数化 GenericThingyWidget,我看不出有任何解决方法。

    我会做什么:参数化GenericThingyWidget,并创建一个工厂静态参数化方法:

    public static <T> GenericThingyWidget<T> make(T someObject){
        ...
    }
    

    【讨论】:

    • "...没有将 GenericThingyWidget 变成泛型"
    • 对不起,我无法实现任何愿望......其他人已经给出了避免警告的可能性。我的解决方案很干净,我不假装它最适合 OP,只是他可能没有想到的另一首曲目。
    【解决方案2】:

    我看到了两种可能性。

    私人添加到GenericThingyWidgetGoetz's capture helper pattern:

    public void applySelectedValue() {
      helper(mThingy, mBox.getSelectedIndex());
    }
    
    private static <T> void helper(GenericThingy<T> pThingy, int pIndex) {
      pThingy.setValue(pThingy.mCandidates[pIndex]);
    }
    

    或者,快速而肮脏,修改GenericThingy的API:

    public void setValue(int value) {
      mValue = mCandidates[value];
    }
    

    作为附加问题,为什么对 mThingy.setValue 的替换调用不起作用?

    Brian Goetz 的文章可能比我更好地解释了这一点,但我会试一试。

    mThingy.setValue(mThingy.mCandidates[mBox.getSelectedIndex()]);
    

    编译器知道mThingy 有一些类型参数,但它不知道那个类型是什么,因为它是一个通配符。它为此类型创建了一个占位符——“capture#4-of ?”。编译器也知道mCandidates 有某种类型,但它也不知道它是什么。它创建了全新的“捕获”类型——“capture#5-of ?”虽然您和我可以推断它们应该是相同的类型,但编译器(至少目前)无法得出这个结论。因此,您会收到错误消息。

    捕获助手解决了这个问题。虽然编译器不知道类型是什么,但它知道它有一个类型,所以它允许你将它传递给辅助方法。一旦进入辅助方法,就没有通配符了,编译器也不需要对通配符是否真的引用相同的类型进行任何推理。

    【讨论】:

    • 感谢您的好答案和有用的链接!编译器无法确定来自同一个对象的两个单独的通配符应该具有相同的捕获,这在我看来仍然很奇怪,但至少这解释了为什么它不起作用。
    【解决方案3】:

    更新

    好的,试试这个:

      public class GenericThingy<T> {
        private Class<T> mClazz;
        private T mValue;
        public final T[] mCandidates;
    
        public GenericThingy(Class<T> clazz, T[] pCandidates, T pInitValue) {
          mClazz = clazz;
          mCandidates = pCandidates;
          mValue = pInitValue;
        }
    
        public void setValue(Object newValue) throws ClassCastException {
          mValue = mClazz.cast(newValue);
        }
      }
    

    【讨论】:

    • @Yishai:是的,但是在以前的 Java 版本中,只使用一个 >,IIRC 会出现一些问题。无论如何,我现在已将答案更改为不同的答案。
    • Hmmm... 看起来像是消除烦人警告的解决方法,但并没有真正遵循严格的架构。
    • 这不是需要烦人的 Class 实例的情况。
    【解决方案4】:

    您需要像这样参数化 GenericThingyWidget:

    public class GenericThingyWidget<T> {
    
    private final GenericThingy<? super T> mThingy;
    private final JComboBox mBox;
    
    public GenericThingyWidget (GenericThingy<? super T> pThingy) {
      mThingy = pThingy;
      mBox = new JComboBox(pThingy.mCandidates);
      //do stuff here that makes the box show up
    }
    
    //this gets called by an external event
    public void applySelectedValue () {
      mThingy.setValue((T) mBox.getSelectedItem());
     }
    }
    }
    

    从技术上讲,您不需要 ?以超级 T 为例,只需要一个 T 就可以了,如果您想从 GenericThingy 中获取而不是仅仅插入它,那么在实际代码中可能会更好。

    【讨论】:

    • "...没有将 GenericThingyWidget 变成泛型"
    【解决方案5】:

    正如 KLE 所说,您可以取消参数化 GenericThingy(将所有 T 替换为对象)。事实上,我认为你必须这样做,除非你打算将 T 的类传递给 GenericThingyWidget 的构造函数,然后从你的 mbox.getSelectedItem() 动态转换,因为据我所知,getSelectedItem() 只返回 Object。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-04
      • 1970-01-01
      • 1970-01-01
      • 2019-08-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多