Java最佳实践-返回对象 vs. 泛型

5
我第一次在学校项目中使用泛型,并遇到了一个哲学上的困境,即是否在我的方法中返回对象或已声明的泛型元素。我的强迫症告诉我需要始终返回已知类型,但我发现这样做会在将基本数据类型输入类时创建一些下游烦恼(当然,在此项目中,我只将基本数据类型输入到该类中)。以下是我所说的例子:
public class DansPriorityQueue<E extends Comparable> 
{
    private ArrayList<E> tree;

//Here's a method that returns an object
public Object peek() {
    return tree.get(0);
}

//Here's a method that returns the generic type
public E peek() {
    return tree.get(0);
}

作为一种补充说明.. 我需要自己实现这个JDK类,但幸运的是我不需要实现真正的PriorityQueue所使用的相同接口,因此我可以选择是否要使用Object或泛型。

我的问题

这让我感到有点不舒服,但我很想只返回一个Object而不是我的E泛型元素,因为当我返回E时,JUnit会强制我将整数值转换为int类型:

DansPriorityQueue<Integer> dpq = new DansPriorityQueue<Integer>();
dpq.add(1);
assertEquals("Expected different value", (Integer) 1, dpq.peek());

当我返回一个对象时,自动装箱不会强制我进行基本类型转换。以下是对我面临的问题更为优美的描述:

http://www.aschroder.com/2009/10/php-1-java-0-the-method-assertequalsobject-object-is-ambiguous-for-the-type/

------------编辑----------------

当我返回泛型类型并使用自动装箱的整数对象填充列表时,如果没有进行上述强制转换,我会收到以下实际错误:该方法assertEquals(String,Object,Object)对于DansPriorityQueueTest类型是模糊的。

--------- 结束编辑--------------

问题

  1. 有人能告诉我为什么应该或不应该返回对象,而不是我正在处理的通用元素吗?两者似乎都有优缺点...最佳实践是什么?

  2. 我大致知道返回对象可能会在以后引起一些转换问题,但我还没有遇到这些问题...有人有具体的危险示例吗?

  3. 在JDK中,我注意到许多集合方法默认返回对象。这是因为泛型是在Java的较新版本中引入的,还是Sun Systems做出的有意决定?


你能不能只是将泛型参数化为Integer,然后通过自动装箱的魔力让它为你工作呢? - InfernalRapture
2
dpq.size() 声明了什么作为其返回类型?我有点困惑,因为您的示例使用了一个似乎根本没有使用 peek() 的调用,所以不确定我是否理解其相关性。size() 不是集合元素类型上通用的方法。 - Mark Peters
1
@DanK,我不明白你的问题。返回一个对象将强制每个人都转换你的返回类型,这是一件非常麻烦的事情。泛型允许在编译时检查类型,否则你需要在代码中处理类型安全性。如果你问如何处理你的JUnit测试,通常没有标准。测试代码可以偏离最佳实践,如果它使生活更轻松,但生产代码不应该这样做。 - InfernalRapture
1
关于JUnit。assertEquals()有几个重载版本,例如:assertEquals(Object, Object)assertEquals(int, int)。当自动装箱使得你的意思不明确时,需要进行强制类型转换,例如:assertEquals(42, new Integer(42))。编译器应该将第一个装箱还是将第二个拆箱?(记住,编译器并不知道实际上这并不重要)。我通过让所有单元测试都扩展我的自己的BaseTest extends TestCase类来解决这个问题,并使用assertInteger(int exp, int act) { assertEquals(exp, act); } - Paul
@Paul - 这实际上是我见过的最好的错误解释。谢谢...真的!那是很好的建议。 - user2858650
显示剩余4条评论
1个回答

8
有人能告诉我为什么我应该或者不应该返回一个对象,而不是我正在使用的通用元素吗?两者似乎都有优劣势...哪种是最佳实践?
这要视情况而定。在这种情况下,您需要通用类型 - 否则为类定义通用类型有何意义?
我模糊知道返回Object可能会导致一些转换问题,但我还没有遇到过它们...有人可以给出具体的危险示例吗?
当然可以!
DansPriorityQueue<String> queue = new DansPriorityQueue<String>();
//add items
Float f = (Float)queue.getObject();  //uh-oh! this compiles but will fail 
Float f = queue.getObject(); //generic type, fails during compile

在JDK中,我注意到许多集合方法默认返回Object。这是因为Java的后续版本引入了泛型还是Sun Systems做出的有意决定?
这主要是为了向后兼容或者在你真正需要使用集合来包含不同类型值的情况下(例如,混合使用JLabel、String和Icon来呈现JTable),才会采用这种方式。
assertEquals("Expected different size", (Integer) 2, dpq.size()); 我认为这不应该成为问题。dpq.size()应该只返回一个int,无论优先队列中存储的是什么。它不会是一个通用值。
你可以创建类似于
DansPriorityQueue<Double> queue = new DansPriorityQueue<Double>();
for(double d = 0; d < 10; d+=1.0)
    queue.add(d);

并且这不应该会有任何问题,对吗?

谢谢Markus,这非常有帮助。不过我之前列出了错误的测试方法。当我对自动装箱的整数进行peek并返回显式泛型类型时,除非我进行强制转换,否则会出现以下错误:The method assertEquals(String, Object, Object) is ambiguous for the type DansPriorityQueueTest。 - user2858650
当然,如果你有两种返回类型可用,那么它就是模棱两可的。但是,如果你删除公共对象peek()方法,就不会再有任何歧义了。 - Markus Koivisto
我只是举例说明了两种peek方法。在我的项目中只有一个peek方法。混淆的原因是因为我在测试中列出了一个int值,但我的类返回一个Integer对象。由于某种原因,它无法协调这两者。请参见上面的链接以获取更多详细信息。 - user2858650
1
好的,如果你想让结果看起来更漂亮,你可以这样做:int value = dpq.peek(); assertEquals("Expected different value", 1, value); 这应该解决问题。这是自动拆箱的一个边角案例,你遇到了这个问题。 - Markus Koivisto
这很有道理...谢谢马库斯! - user2858650

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接