【问题标题】:java generic class & wildcardsjava泛型类和通配符
【发布时间】:2015-07-10 19:26:07
【问题描述】:

我有一个具有以下功能的 Box 泛型类:

  • 2 个put 方法之一应允许客户端插入一个框并将其内容获取到当前框。

我希望这个方法得到 2 种类型的框:Box<Number>Box<Integer>,这就是我将 put(Box<T>) 方法更改为 put(Box<? extends Number> box) 的原因。但我收到编译警告。我在这里做错了什么?

这是我当前的代码: 我得到的警告是:类型安全:来自 capture#1-of 的未经检查的强制转换?将 Number 扩展到 T

public class Box<T> {
    public T get() {
        return element;
    }

    public void put(T element) {
        this.element = element;
    }

    public void put(Box<? extends Number> box) {
        put((T) box.get()); // this is where i get the warning
    }

    private T element;
}

public class BoxClient {

    public static void main(String[] args) {
        Box<Number> nBox = new Box<Number>();
        Box<Integer> iBox = new Box<Integer>();
        nBox.put(iBox);

    }

}

【问题讨论】:

  • 当您有错误时,请在您的问题中包含错误。
  • “但我收到编译警告”你能分享一下吗?
  • T 不扩展Number,因此会出现编译器警告;你正在做一个不安全的演员。

标签: java generics


【解决方案1】:

改成

public void put(Box<? extends T> box) {
    put(box.get());
}

【讨论】:

  • 谢谢你,我现在知道我做错了什么。感谢帮助! :)
【解决方案2】:

如果我测试您的代码,那么我会在此方法中收到警告:

public void put(Box<? extends Number> box) {
    put( (T) box.get());
}

警告是:

未经检查的演员表:java.lang.NumberT

问题是您的泛型类型T 没有bounds,所以它可以是任何东西,并且由于type erasure,它将被删除为Object

变量box 的泛型类型有一个界限,所以很明显,它总是某种Number

您的演员(T) box.get() 现在是“不安全的”,因为box.get() 返回Number 的子类型,而T 可以是任何东西,例如String。所以编译不能确定这个演员总是没问题并且不会抛出ClassCastException。 这就是为什么他警告你关于你的不安全演员表的原因。

【讨论】:

  • 我的编译器没有给出警告。
【解决方案3】:

您的类Box 定义了一个泛型类型TT 可以是任何东西。 IE。 "一个包含任何种对象的盒子"

然后,您的方法会收到一个Box&lt;? extends Number&gt;。 IE。 “一个包含数字类型的盒子”。

问题出在你的方法上:

public void put(Box<? extends Number> box) {
    put(box.get());
}

该方法接收“一个包含数字的盒子”,但是,它会强制转换为T。 请记住,T 可以是任何东西,因此您可能会对实际上不是数字的东西执行强制转换。考虑这个例子:

public class BoxClient {

    public static void main(String[] args) {
        Box<String> sBox = new Box<String>();
        Box<Integer> iBox = new Box<Integer>();

        iBox.put(1);
        sBox.put(iBox);

        System.out.println(sBox.get()); 
    } 

}

在这里,我创建了一个Box&lt;String&gt; 或“包含字符串的盒子”并添加了一个Box&lt;Integer&gt; 或“包含数字的盒子”。 通过泛型代码,这是有效的,因为 T 可以是任何东西,但是,如果您尝试执行它

java.lang.ClassCastException: java.lang.Integer 无法转换为 java.lang.String

这是因为 Integer 1 不能转换为 String

为确保这种情况永远不会发生,您需要更改Box 的声明以强制执行T extends Number,如下所示:

public class Box<T extends Number> {
    public T get() {
        return element;
    } 

    public void put(T element) {
        this.element = element;
    }

    public void put(Box<? extends T> box) {
        put((T) box.get());
    }

    private T element;
}

更新: 如前所述,我之前的回答可能仍然不安全。 比如说,如果你有一个Box&lt;Number&gt; 并尝试向它添加一个Box&lt;Integer&gt;,它仍然会被允许并且仍然会引发异常。

最好的解决方案是添加bayou.io 的建议。 这样,您将确保: 1:你的盒子总是包含数字 2:您的 Box 只能从扩展为 T 的其他 Box 添加

更新2: 正如下面 newacct 在 cmets 中提到的那样,也不需要强制转换为 T

希望这会有所帮助!

【讨论】:

  • 这很有帮助。谢谢 :) 只是为了确保,您建议的解决方案意味着泛型类现在只获取 Numbers 或其子类而没有其他类型?
  • 没错!您定义了T 不再可以是“Anything”,而是,T 只能是扩展 Number 的类。
  • 如果这不是您想要的,您可以使用 bayou.io 的回答来强制您的方法将始终收到扩展 T 的内容
  • 知道了!感谢您的提醒,我编辑了答案。
  • @Sekkuar: box.get() 已经返回 T 所以你不需要转换为 T
【解决方案4】:

你的 Box 有一个泛型类型 T,它不假设它的类型(没有界限)。如果您的框只能包含Numbers(和Number 的子类型),您可以将其更改为:class Box&lt;T extends Number&gt;。 以及您的方法:put(Box&lt;? extends T&gt; box),以便它接受 T 及其任何子类型。

【讨论】:

  • 如果我选择只更改 put 方法的参数类型怎么办?这意味着我让课程更通用,但我对数字和整数的需求仍然没有得到处理?
  • 是的,当然。这更通用,也适用于数字和整数。
【解决方案5】:

您的问题是因为泛型类型是在 Java 5 中引入的。因此,方法签名不包含关于 泛型类。

班级草稿:

public abstract class Draft<T> {
    public abstract void put(T element);
    public abstract void putDraft(Draft draft);
}

有了这个草稿,代码保持可读性,你没有任何编译器 警告。

查看here 以查找有关此问题的更多信息。

【讨论】:

  • 你的回答很模糊。你能解释一下你的意思吗?
  • 谢谢,那么我如何使用满足我需要的 put 方法来实现泛型类?我确定我必须使用通配符,我只是不太确定如何使用。
猜你喜欢
  • 1970-01-01
  • 2011-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多