Java通用问题:有更好的方法吗?

时间:2009-04-29 03:11:58

标签: java generics

[更新]:我的初步示例并未反映我的问题。更新了样本,现在更好了。

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
    }
  }
}

两种方式都很糟糕!还有更好的办法吗?

4 个答案:

答案 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。您将该保护程序传递给该函数,并以多态方式调用。

您可能仍然有一个开关,但至少现在您将其隔离为工厂方法。