[更新]:我的初步示例并未反映我的问题。更新了样本,现在更好了。
Java Generic和重载不能很好地协同工作。
public interface Request<E> {
E getValue();
}
我有一个如上所述的通用参数化Request接口,我想写一个DataStore类来保存有效负载请求进位。对于不同的有效负载类型,保存逻辑是不同的。
我真正想要的是像
public interface DataStore {
void save(Request<E1> r);
void save(Request<E2> r);
}
但这当然是非法的。
如果我想保留类型安全性,DataStore必须为我想要处理的每种类型声明不同的方法,例如
public interface DataStore {
// here E1 is the actual type I want to handle, not a generic parameter
public void saveE1(Request<E1> e);
public void saveE2(Request<E2> e);
}
或牺牲类型安全
public class DataStore {
public void save(Request e) {
Object value = e.getValue();
if (value instanceof ReqE1Impl) {
// do something
} else if (value instanceof ReqE2Impl) {
// do something else
}
}
}
两种方式都很糟糕!还有更好的办法吗?
答案 0 :(得分:4)
更好的方法是让对象知道如何保存自己。如果您仍想使用DataStore,可以执行以下操作:
interface YourInterface {
void save();
}
interface Request<E extends YourInterface> {
E getValue();
}
class DataStore<E extends YourInterface> {
public void save(Request<E> r) {
r.getValue().save();
}
}
你必须确保你的对象然后每个都实现interface
和中提琴,你的泛型和多态性相互配合。
注意:编写(修订)问题的方式,您没有使用多态。您正在使用重载。方法重载在编译时决定,而方法多态在运行时决定。这是你遇到这些困难的部分原因。
如果你真的不想要上述类型的设计,其中对象完成了自救的所有工作,那么也许你可以做这样的事情(作为一个非常弱的例子):
interface YourInterface {
String serialize();
}
interface Request<E extends YourInterface> {
E getValue();
}
class DataStore<E extends YourInterface> {
public void save(Request<E> r) {
String value = r.getValue().serialize();
// Now do something with value to save to a datastore
}
}
上面是一个弱示例,但主要思想是对象知道如何执行对象之间不同的序列化部分,然后DataStore可以执行所有对象共有的工作。
是的,你是对的(用错误的术语) - 泛型不适合重载。但他们确实在多态性方面表现得很好。无论如何,多态性通常是比过载更好的工具。
答案 1 :(得分:3)
也许我误解了这个问题,但是仿制药似乎解决了这个问题:
public interface Handler<E> {
void save (E obj, DataStore<E> store);
}
public interface DataStore<E> {
public void save (E obj);
}
public class AHandler implements Handler<String> {
public void save (String obj, DataStore<String> obj2) {
}
}
public class BHandler implements Handler<Boolean> {
public void save (Boolean b1, DataStore<Boolean> obj3) {
}
}
答案 2 :(得分:2)
我认为没有其他方法可以实现您尝试使用该设计的方式。
在不知道E
类型的细节的情况下,我只能提出以下一般性建议:
DataStore
可能只应处理E
的特定子类型?您可以参数化DataStore
接口以确保类型安全,即每个Handler<E>
接受DataStore<E>
。
或者,考虑将保存功能移动到E
类型的实现中 - 这样,DataStore
无需关注E
的确切类型
答案 3 :(得分:1)
我认为问题不在于泛型,而在于设计。
您所说的是您传递的是E的子类的实例,并且基本上需要运行与该特定类相对应的特殊处理程序。所以你基本上有两个并行的层次结构。这是一个相当常见的设计问题。
如果您使用工厂方法创建Es,那么您应该有一个工厂方法,它根据E获取相应的SaveToDatastoreHandler。您将该保护程序传递给该函数,并以多态方式调用。
您可能仍然有一个开关,但至少现在您将其隔离为工厂方法。