【问题标题】:Java - Instantiating objects of type X when all you have is the reference typeJava - 当您只有引用类型时实例化类型 X 的对象
【发布时间】:2010-12-13 22:49:53
【问题描述】:

我快速浏览了建议的“相关问题”,但找不到与我所问问题直接相关的问题。即使有,我仍然很感激您对最佳方式的意见。

首先是一些上下文。
我正在扩展一个 Java 应用程序,我有以下接口和类:

public interface Mapper {
  public void foo();
}

public class SomeGrammar implements Mapper {

  public SomeGrammar() {}

  public SomeGrammar(SomeGrammar toCopy) {
    //Copy specific fields
  }

  public void foo() {}
}

我正在使用的应用程序在构建时只考虑了一种语法类型,SomeGrammar,因此SomeGrammar 类贯穿整个应用程序。

SomeGrammar 对象通常使用复制构造函数进行复制:

SomeGrammar aGrammar = new SomeGrammar();
SomeGrammar newGrammar = new SomeGrammar(aGrammar);

我正在尝试实现新的语法类型。所以我打算把核心方法拉出来并实现一个Grammar接口,甚至将这些方法合并到Mapper接口中。所以所有的语法类都会实现这个接口。


无论如何,我的问题是,替换上述行的最佳方法是什么,以便我可以概括复制当前恰好被接口类型引用的任何语法对象?

显然我做不到

Grammar newGrammar = new Grammar(aGrammar);

因为Grammar 是接口的类型,而new Grammar(aGrammar) 没有意义。我想我可以在我的界面中添加一个克隆方法来代替复制构造函数,但正如我之前所说,我很感谢你对此事的看法。

提前致谢,
欧文

【问题讨论】:

    标签: java interface copy types


    【解决方案1】:

    如果我正确理解了您的问题,我的解决方案将如下所示:

    假设我们想要创建 3 个具体的语法,即 GrammarA、GrammarB 和 GrammarC。我们将定义这些类中的每一个来实现 Grammar 接口。 GrammarInterface 将定义您的 foo() 函数,以及一个 doCopy() 函数。然后,每个具体的 Grammar 类都将负责实现 foo() 功能并通过调用复制构造函数返回自身的副本。这是你要找的吗?

    interface IGrammar { 
    
        public void foo();
        public Grammar doCopy();
    
    }
    
    class GrammarA implements IGrammar { 
    
        public Grammar doCopy(Grammar g) { 
    
            return new GrammarA(g);
    
        }
    
    }
    

    【讨论】:

    • Java 的协方差允许 GrammarA 的 doCopy 返回一个 GrammarA 的实例。
    • 是的,您理解正确。我所说的“克隆方法”是指一种创建对象副本的方法(就像您向我展示的那样)。抱歉我的术语不好,答案很好:-)
    • Eoin,如果这可以解决您的问题,您可能希望选择答案作为“已接受”的解决方案。
    【解决方案2】:

    Object.clone() 不好,请参阅 Effective Java 进行讨论。 标记接口不好(请参阅 Externalizable、Serializable 等)有大量库试图规避这些无用的标记接口。 Cloneable<T> 可能是一种方法。

    我建议Factory

        Grammar someGrammar = Factory.newGrammar();
        Grammar copyOf = Factory.copyGrammar(someGrammer);
    

    这样做的原因是,如果它是可克隆的,Factory 可以委托给语法对象,但它也可以做其他事情,例如保留创建语法的列表或返回适配器或返回缓存的实例(通常,独立于实际的语法实现)。此外,实现类不需要在整个应用程序中直接使用。

    【讨论】:

    • 这取决于是否希望能够将能够复制语法与语法实现分开,所以我已经 +1 了你和 Amir Afghani 的答案。
    【解决方案3】:

    您可以添加clone() 作为接口的一部分,并让具体类正确实现它。然后您的客户端代码将如下所示:

    Grammar aGrammar = new SomeGrammar();
    Grammar newGrammar = aGrammar.clone();
    

    查看一些解释here。就个人而言,我只建议将您自己的copy() 方法作为Grammar 接口的一部分。这比重复使用clone()恕我直言要好。

    【讨论】:

    • 这对于实例化不同类型的语法没有帮助:AnotherGrammer newGrammear = new AnotherGrammer(aGrammar);这是否真的是 OP 应该做的事情是另一回事。
    • 我认为上面的答案会起作用。当对象的引用类型是接口的引用类型时,我要做的就是创建对象的副本(其类实现接口)。因此,只要每种不同类型的语法类都正确实现了 clone(),那么这肯定没问题。是的,其他人表示使用 copy() 方法会比 clone() 更好。
    【解决方案4】:

    我认为你想要的是让Grammar 接口包含makeCopy 签名。

    然后制作副本将如下所示:

    Grammar newGrammar = oldGrammar.makeCopy();
    

    避免使用clone 方法,因为那样会很疯狂!

    【讨论】:

      【解决方案5】:

      您还可以使用反射来查找 Grammar 的类型并使用匹配的构造函数创建该类型的新实例:

      interface Interface {
      }
      
      class A implements Interface {
          public A(Interface other) {
      
          }
      }
      
      class B implements Interface {
          public B(Interface other) {
      
          }
      }
      
      public class Test {
          public static Interface newWithExactType(Interface i) throws Exception {
              Class<? extends Interface> c = i.getClass();
              Constructor<? extends Interface> ctor = c.getConstructor(Interface.class);
              return ctor.newInstance(i);
      
          }
          public static void main(String[] args) throws Exception {
              Interface a = newWithExactType(new A(null));
              Interface b = newWithExactType(new B(null));
              System.out.println("Type of a: "+a.getClass().getSimpleName());
              System.out.println("Type of b: "+b.getClass().getSimpleName());
          }
      }
      

      【讨论】:

        【解决方案6】:

        如果您确实需要在多个不同实现之间进行复制,我建议采用以下方法:

        public interface Mapper {
            void Mapper();
            void Mapper(Mapper copy);
        
            Foo getFoo();
            Bar getBar();
            // getters for shared properties which can be copied between implementations
        
            void doStuff();
        }
        
        public abstract AbstractBaseMapper implements Mapper {
            protected Foo foo;
            protected Bar bar;
        
            public void Mapper() {}
        
            public void Mapper(Mapper copy) {
                 foo = copy.getFoo();
                 bar = copy.getBar();
            }
        
            public Foo getFoo() { return foo; }
            public Bar getBar() { return bar; }
        }
        
        public ConcreteMapper extends AbstractBaseMapper {
            public void doStuff() {
            }
        }
        
        public AnotherMapper extends AbstractBaseMapper {
            public void doStuff() {
            }
        }
        
        public YetAnotherMapper implements Mapper {
            // NB: this one is a straight implementation of the Mapper interface, doesn't inherit from AbstractBaseMapper...
        }
        

        现在您可以执行以下操作:

        Mapper m1 = new YetAnotherMapper();
        Mapper m2 = new ConcreteMapper(m1);
        Mapper m3 = new AnotherMapper(m2);
        

        这种方法的缺点是您必须在基本接口中定义所有可复制字段,以便始终保证实现它们。另一种方法是使用 Karl 建议的反射。您还可以查看Dozer,它会自动进行此类反射属性复制。

        【讨论】:

          【解决方案7】:

          IMO 最好的选择是拥有 public interface Mapper extends Cloneable { public Object clone(); }

          并让该方法调用super.clone() 并在相关时执行深度克隆(原则上只有实现类知道这一点)。

          诉诸反射率可能会影响性能并使扩展设计更加复杂(太?)。这里的另一种选择是提供一个具有复制构造函数而不是接口的抽象类,但这有明显的限制,可能是一个显示停止器。

          【讨论】:

          • 不再推荐使用 clone()(例如,参见 Joshua Bloch 的 Effective Java
          猜你喜欢
          • 2013-02-15
          • 2013-09-18
          • 1970-01-01
          • 2018-03-20
          • 1970-01-01
          • 2013-05-01
          • 1970-01-01
          • 2018-01-02
          • 1970-01-01
          相关资源
          最近更新 更多