【问题标题】:Java Generic Question: Any better way?Java 通用问题:有更好的方法吗?
【发布时间】:2009-04-29 03:11:58
【问题描述】:

[更新]:我最初的例子没有反映我的问题。更新了示例,希望现在更好。

Java 泛型和重载不能很好地配合使用。

public interface Request<E> {
  E getValue();
}

我上面有一个通用的参数化Request接口,我想写一个DataStore类来保存payload请求携带。不同payload类型的保存逻辑不同。

我真正想要的是类似的东西

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

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

【问题讨论】:

    标签: java generics


    【解决方案1】:

    更好的方法是让对象知道如何自救。如果您仍想使用 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 和 viola,泛型和多态可以相互配合。

    注意:您(修订后的)问题的编写方式,您没有使用多态性。您正在使用重载。方法重载是在编译时决定的,而方法多态性是在运行时决定的。这就是您遇到这些困难的部分原因。

    如果你真的不想要上面那种让对象自己做所有工作的设计,那么也许你可以做这样的事情(作为一个非常弱的例子):

    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 可以执行所有对象共有的工作。

    是的,你是对的(使用错误的术语)——泛型不能很好地处理重载。但是它们确实很好地处理了多态性。无论如何,多态性通常是比重载更好的工具。

    【讨论】:

    • 赞成。您正确地指出我修改后的示例实际上是关于重载。
    【解决方案2】:

    也许我误解了这个问题,但泛型似乎可以解决这个问题:

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

    【讨论】:

      【解决方案3】:

      我认为没有其他方法可以实现您想要通过该设计实现的目标。

      在不了解E类型具体情况的情况下,我只能提出以下一般性建议:

      • 可能DataStore 应该只处理E 的特定子类型?您可以参数化DataStore 接口以确保类型安全,即每个Handler&lt;E&gt; 接受DataStore&lt;E&gt;

      • 或者,考虑将保存功能移动到您的 E 类型的实现中 - 这样,DataStore 不需要关心 E 的确切类型。

      【讨论】:

      • 但是E这个数据对象应该不知道如何保存自己。这是 DataStore 的责任。
      • 确实如此 - 在这种情况下,也许 DataStore 接口过于笼统?只定义一个 save(E) 方法怎么样?该接口的实现将处理特定类型的 E?
      • “应该”从哪里来?更重要的是,为什么除了 E 的实例之外的任何东西都知道如何保存 E 的实例?
      【解决方案4】:

      我认为问题不在于泛型,而在于设计。

      您的意思是您正在传递 E 的子类的实例,并且本质上需要运行与该特定类对应的特殊处理程序。所以你基本上有两个并行的层次结构。这是一个相当普遍的设计问题。

      如果您使用工厂方法创建 Es,那么您应该有一个工厂方法,该方法根据 E 获取适当的 SaveToDatastoreHandler。您将该保护程序传递给该函数,它会以多态方式调用。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-01-31
        • 2023-01-27
        • 1970-01-01
        • 2017-03-24
        • 1970-01-01
        • 2013-05-03
        • 2011-06-30
        • 1970-01-01
        相关资源
        最近更新 更多