我想展示一个场景,最好:
- 有一个通用的界面,
- 有各种通用基类来实现它,
- 为通用基类提供子类,
- 以某种方式获得子类的非通用接口(interface)。
感到困惑吗?继续阅读并告诉我您对该解决方案的看法。
假设我想要一个服务基于任何类型的 key 提供任何类型的对象 以及来自任何地方(数据库、文件等)。
为了一个好的开始,让我们创建一个合适的界面:
public interface ObjectProvider<K, V> {
V provide(K key);
}
通过这种方式,我将能够提供任何类型的实现。 我想提供来自数据库和文件的对象。
假设可以使用相同的方法来提供基于 K 的 V 无论对象类型如何,数据库逻辑。所以我可以编写一个通用基类 对于数据库访问:
public class DBObjectProvider<K, V> implements ObjectProvider<K, V> {
public V provide(K key) {
V v = null;
//some common DB logic to get V based on K
return v;
}
}
实际上,从文件中获取对象也是与对象类型无关的:
public class FileObjectProvider<K, V> implements ObjectProvider<K, V> {
public V provide(K key) {
V v = null;
//some common file reading logic to get V based on K
return v;
}
}
好吧,现在我有两个泛型类,我可以用它们来获得我想要的任何东西。
现在,我想使用这些通用实现之一来根据数据库中的 String 键获取 String 对象。另外,我想使用 Spring XML 将其定义为一个 bean。 我想没有办法在 Spring XML 中定义通用 bean(我是对的吗?)所以我 将创建一个适当的类。我需要做的就是:
public class DBStringStringProvider extends DBObjectProvider<String, String> {
}
现在我可以将此 bean 注入(inject)到任何:
private ObjectProvider<String, String> objectProvider;
一切都很好,现在是关键部分。 我可以通过多种方式利用有用的 DB String-String 提供程序(嗯……好吧,不是真的)。假设我想用它来做一个网络服务。 假设我想将 DBStringStringProvider 公开为 CXF Web 服务并对其进行测试。 问题是此类 Web 服务的客户端代码如下所示:
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(<INTERFACE>.class);
<INTERFACE> client = (<INTERFACE>) factory.create();
所以我需要一个 DBStringStringProvider 的非通用接口(interface),但我没有,因为它扩展了它的通用基类。
我可以执行以下操作:
用另一个接口(interface)扩展 ObjectProvider
@WebService
public interface StringStringProvider extends ObjectProvider<String, String> {
@WebMethod
String provide(String key);
}
另外在通用基类已经实现的地方实现它:
public class DBStringStringProvider extends DBObjectProvider<String, String>
implements StringStringProvider {
}
对于实现基类已经引入的相同接口(interface),我感到有点不舒服。 但这样我就可以使用 WS 客户端了:
factory.setServiceClass(StringStringProvider.class);
StringStringProvider client = (StringStringProvider) factory.create();
我的问题是:我刚才所做的事情是一个好的做法吗?如果不行还有其他办法吗?
这只是一种场景,如果有一个非通用接口(interface)就好了 这已被定义为通用。
最佳答案
我不愿意批评所提出问题的抽象程度的动机。为了接口(interface)的一致性和减少重复代码,这些场景时有发生。 Java,尤其是令人讨厌的服务框架,使这项任务比实际需要的复杂得多;这种不必要的复杂性并不意味着最初的设计很糟糕。每天,Java 都让我因各种与毫无意义的冗长相关的原因而咬紧牙关。
我很高兴地确认定义一个无偿的具体子接口(interface)是可以接受的且常见的做法。您被迫采取此操作是因为您正在解决服务框架中的限制(不允许参数化接口(interface))。
唯一需要关心的是您确实在尝试在 Java 中执行typedef。我能做的最好的事。 Java 中的 typedef 会导致极其丑陋的结构,这会让您原始帖子的评论者(从他们对复杂性的厌恶来看)想要衡量自己的眼睛。
我怀疑在实践中你会遇到任何问题,但总的来说psuedo-typedefs像这个例子可能会引起意想不到的痛苦。在一般情况下,当您创建一个简单的子接口(interface) T
时接口(interface)S
,很容易使用 T
代替S
无论它适用于何处。 S
例如,可能是一个冗长的参数化 Java 类表达式,阅读或输入起来很痛苦,并且 T
可能又好又短。但对于方法/服务调用/构造函数参数,您仍然需要 S
作为声明的类型,否则您将面临限制方法或类的实用性的风险。一个人为的例子:
public interface Info extends Map< String, List< FooBar > > {}
public interface Service {
public Info doSomething( Info info );
}
没有理由Service
只接受Info
,如Info
未向 Map< String, List< FooBar > >
添加 API ;正如所写的, doSomething 的调用者不能将任何旧 map 作为输入;他们必须创建一个新的 Info
以某种方式使用该映射的内容对其进行初始化,因为 Java(与几乎所有其他广泛使用的企业语言一样)使用主格类型。
关于java - 扩展泛型类并第二次实现其接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10862859/