泛型 vs 接口

15

我从Java 1.4(之前的公司)转移到了Java 1.6(新公司)。我发现,在1.4中,大多数专有框架都是使用接口和模板模式定义的,而在1.6中,大多数框架都是围绕泛型定义的。

虽然我仍在努力掌握泛型,但我的问题是 - 这是正确的设计方法吗?接口使你的设计更加灵活/解耦。而泛型实现了类型安全,并强制你传递特定类型的类。这并不真正有助于解耦你的代码。这样正确吗?

一个例子 -

public MyWizard extends SignupWizard<SignupSection, SignupObject, SignupListener, SignupView>{
}

如果设计更具灵活性,那就应该...

public interface Wizardable{
  public SignableSection getSection();
  public SignableObject getSignableObject();
...
}

public MyWizard implements Wizardable{
....
}

1
请改进您的示例。例如,什么是SignupWizard的定义?给出一些具体的框架示例,说明泛型的使用方式。 “泛型与接口”的问题对我来说没有多大意义,因为通常两者都会一起使用。“注释vs.接口”更有意义。 - Esko Luontola
看起来这是我期望在C++中看到的东西,而不是Java。 - Tom Hawtin - tackline
5个回答

6
我不会说泛型与接口哪个更好,它们各有不同的需求和用途。在原始帖子中提到的使用泛型参数可以实现多种目的。它允许开发人员定义组成类的字段对象类型的基类。使用此方法,开发人员可以将类对象作为参数接受,然后可以对其进行反射以创建实际对象并设置字段,或者仅接受整个对象以首先进行设置。需要类对象而不是执行new T()之类的操作所涉及的问题称为类型擦除
使用泛型的另一个好处是,在使用超类的字段或方法时,您不需要一直进行类型转换 -- 您个人知道它们的类型,但Java没有将该类型信息存储在任何地方。另一个好处是,所有getter/setter方法都可以使用泛型参数,并向依赖于您在上述对象中设置了专门字段的其他对象公开更合理的前端。
使用接口来完成泛型所做的相同事情的问题在于,您需要额外的方法来访问专门的类型并将它们强制转换后再返回它们(或检查传入的类型,然后将字段设置为对象)。这使得设计变得更加复杂,而且并没有真正帮助解耦。
正如我在评论中提到的那样,任何子类都将设置这些类型参数并向用户公开。因此,您可以拥有像class MegaSignupWizard extends SignupWizard<MegaSignupSection, MegaSignupObject, MegaSignupListener, MegaSignupView>这样的东西,而MegaSignupWizard仍然具有访问类中专门方法的权限,而无需进行任何强制转换。现在这真是太棒了 :)

1
现在这才是一个顶级答案!我更喜欢泛型,因为它不依赖于粘合。这样一切都是明确的。 - vintprox

3

泛型允许你在通用类型上实现方法,而接口只定义签名。有时会被滥用以像Scala的trait一样使用,但它们大多数情况下都有两个不同的目的。如果所有东西都是接口,就会有很多重复的代码或委托给一些辅助类。


1

我注意到许多新的框架倾向于使用注解而不是接口,但我还没有注意到它们使用泛型代替接口(无论它是什么意思 - 请进一步解释)。

通过使用泛型,可以减少一些代码重复,并提高类型安全性,当您有一个带有泛型类型参数的接口时,相同的接口将适用于许多不同的类型。 java.util 包中的集合就是泛型非常有用的一个很好的例子。

在注解的情况下,有时我觉得它们被过度使用了,使用接口会更好 - 例如,使用接口可以轻松知道方法应该采取哪些参数 - 但在其他情况下,注解更加灵活和强大。


注解代替接口?!这样的框架会是什么样子? - Murali VP
1
例如,在Servlet 3.0 API中的@GET注释:http://today.java.net/article/2008/10/08/introduction-servlet-30 - Esko Luontola
@Murali:你听说过JPA吗?几乎只有注释。 - whiskeysierra

1

我认为它涉及到泛型和接口,因为任何接口本身都可以使用泛型。当您开始使用泛型(抽象)类时,我看到的问题是您失去了使用组合的灵活性。虽然继承本身没有什么不好,但您必须为其进行设计,并且存在许多陷阱。


没错,Bloch在《Effective Java》中的一个观点是优先使用组合而非继承。我的理论是,每当我看到大量使用泛型和抽象类时,程序员们可能只是对Java中最新的玩具(泛型、注解)不够熟悉,而不是接口。因此,当我们将所有框架作为答案时,并不一定意味着这是唯一正确的方法。只有时间能证明它们的设计是否坚固且可塑性强,我想,也就是说,它们是否像log4j那样长久存在。 - michaelok

0

看看Jung2,你就会发现泛型变得非常强大。基本上,任何对象都可以是顶点或边,这在网络算法中产生了一些非常有趣的想法。你不能仅仅使用接口来实现这个。

我不确定你上面的用法是否正确。如果你扩展自一个对象或实现一个接口,你不应该像那样传递对象。泛型用于实现具有抽象类型的类,比如集合,可以对任何东西进行操作。如果我传递一个完全有效的SignupWizard<int, int, int, int>会发生什么?

对于这个特定的想法,也许你确实想使用接口。定义一个接口来定义向导动作,每个对象都实现它,mywizard能够 .addaction(int position, wizardaction action)。事实上,这绝对是一个接口问题。

我同意其他人的观点——只有在库需要时才使用泛型。


如果顶点和边有一个超级接口,那么一个对象可以是顶点或边。 - Murali VP
但是你不能在边缘上使用类型化的参数。你必须要么使用对象,要么坚持一种类型。 - whiskeysierra
这是完全有效的。任何子类都将设置这些类型参数并对用户公开。因此,您可以拥有类似于MegaSignupWizard扩展SignupWizard<MegaSignupSection,MegaSignupObject,MegaSignupListener,MegaSignupView>的内容,并且一切仍然完全有效,MegaSignupWizard可以访问类中的专门方法,而无需进行任何转换。 - Chris Dennett

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