【问题标题】:Methods that return Interface ? [given that there are many implementations of interface]返回接口的方法? [鉴于接口的实现有很多]
【发布时间】:2012-04-03 03:05:03
【问题描述】:

这个问题可能很简单(我希望不是),但我是在一次采访中发现的。 一般来说,给定一个接口类 IntClass,以及许多实现名为 ImpIC1、ImpIC2、...、ImpICk 的接口的类。 IntClass 中的方法是抽象的公共方法,名为 void action1()、void action2() ...、void actionM()。

我得到了一个带有公共静态函数的公共类 Test,名为:public static IntClass foo(IntClass c1, IntClass c2)。该方法对 c1 和 c2 进行一些操作以创建一个 InClass 并返回。 但是,该方法必须适用于 IntClass 的每个有效实现。问题主要在于c3的定义。我将如何设计方法 foo() ..

代码详情见下文:

public interface IntClass { 
 abstract void action1(); 
 .. 
 abstract void actionM();
 }

..

 public class ImpIC1 implements IntClass { 
 public void action1() { ... } 
 public void action2() { ....}
 ..
 public void actionM() { ... } 
 } 

...

 public class ImpIC2 implements IntClass { 
 public void action1() { ... } 
 public void action2() { ....}
 ..
 public void actionM() { ... } 
 } 

...

 public class ImpICk implements IntClass { 
 public void action1() { ... } 
 public void action2() { ....}
 ..
 public void actionM() { ... } 
 }    

...

public class Test { 
public static IntClass foo(IntClass c1, IntClass c2) { 
 ....
 ...
  return c3; 
}
} 

...

问题主要出在c3的定义上。我尝试了以下解决方案(均无效):

  1. 在 foo 中,c3 被定义为 Object 的实例 .. 即 IntClass c3 = (IntClass) new Object()。 -- 编译时错误 Object cannot be cast to IntClass
  2. 在 foo 内部,c3 没有进行初始化。 (编译时错误)。
  3. foo内部,c3为null,(空指针异常)
  4. 在 foo 内部,c3 被初始化为 c3 的实现之一(即 c3 = (IntClass) new ImpIC1()) .. 问题是如果我们在外部 ImpIC2 中执行 s = (ImpIC1) foo(c1, c2) - 我们得到一个错误(无法转换)。

【问题讨论】:

  • c3 应该是什么?它与c1c2 有什么关系?
  • c3 是 IntClass 类型(它是一个接口),由函数 foo(IntCLass c1, IntClass c2) ..返回。
  • 我了解您希望 c3 成为 IntClass。但是里面c3是什么?你是如何结合c1c2 来实现的?如果您只是想返回任何IntClass,为什么不返回return ImplC1()
  • @Ahmed Jedda - 这不是对 c3 应该是什么以及它与 c1 和 c2 的关系问题的信息性答案。我还是不明白为什么return null; 不能解决这个问题。
  • 我们假设每个实现 ImpC1, ImpC2 .. ImpCk 使用不同的数据结构但相同的函数 action1() .. actionM();那么在这里我们希望函数 foo() 可以在任何实现上工作.. YET!我们在公共类 Test 中定义 foo() ! - 真的很奇怪,我想了一天!

标签: java oop interface


【解决方案1】:

我不知道你的实现。但是这里有一些我们需要知道的点。 您不能实例化接口(匿名内部类除外)。如果您希望接口返回更改测试类如下 -

     public class Test { 
            public static IntClass foo(IntClass c1, IntClass c2,String className) { 
 ....
 ...

 IntClass c3 = Class.forName(className);
  //Set properties of c3 as your logic.
  return c3; 

} }

【讨论】:

  • 其实这是一个非常有趣的答案.. 你认为我们可以在不改变函数结构的情况下做到吗?
  • 至少您必须指定类的名称作为参数,以便初始化正确的对象,必须有办法知道要启动哪个类。
  • Class.forName 返回一个Class——不是一个新实例。您需要在返回的类上使用newInstance
  • 您可以使用任一参数的类来创建新对象——以避免更改函数签名。但我质疑结构:如果你返回一个 INTERFACE,使用返回对象的那个不应该转换它,而是按原样使用它。
【解决方案2】:

也许他们想看看你是否知道如何使用reflection 来返回c1 是什么类的实例。每个对象都是 class 的一个实例(甚至是匿名内部类)。您可以为引用的类型提供一个接口,但最终它指向一个 class 实例。

public IntClass foo(IntClass c1, IntClass c2) {
    IntClass c3 = c1.getClass().newInstance(); // requires no-arg constructor
    // use c1 and c2 to set up c3 ...
    return c3;
}

【讨论】:

  • 我认为没有其他方法可以解决它,只能假设这样的事情。
  • @AhmedJedda - 有更好的问题可以用来测试某人的反思知识,特别是因为你不可能知道newInstancereturn 之间应该怎么做。无法将每对 actionN 方法的返回值进行一般组合。这是一个糟糕的面试问题。
  • 老实说 .. 它让我修改了我的 Java 知识!! ..但没关系..如果他们会打电话,他们会打电话! [您可以查看上面 Alderath 评论下的 cmets 示例,(据我了解)-也许有帮助]
【解决方案3】:

发布的规格非常模糊。 “此方法对 c1 和 c2 进行一些操作以创建一个 InClass 并返回”。这根本没有指定任何功能要求。

以下解决方案对 c1 和 c2 进行了一些操作(它们是相当无意义的操作,但规范并没有禁止这些操作是无意义的)。它还创建了一个返回的新 IntClass 实例。而且,IntClass 的任何实现都可以作为参数传递,并且该方法仍然有效。

public static IntClass foo(IntClass c1, IntClass c2){
    c1.getClass();
    c2.toString();
    return new IntClass{
            public void action1(){}
            public void action2(){}
            ...
            public void actionM(){}
        };
}

现在,根据我对可能要求的猜测,我将提出另一个更明智的解决方案。这不完全符合规定的要求,但我认为这可能是面试官正在寻找的。​​p>

public class Test{

    public static IntClass foo(IntClass c1, IntClass c2){
        return new CompositeIntClass(c1, c2);
    }

    public class CompositeIntClass implements IntClass{
        private IntClass c1;
        private IntClass c2;

        public CompositeIntClass(IntClass c1, IntClass c2){
            this.c1 = c1;
            this.c2 = c2;
        }

        public void action1(){
            c1.action1();
            c2.action1();
        }

        public void action2(){
            c1.action2();
            c2.action2();
        }

        ...

        public void actionM(){
            c1.actionM();
            c2.actionM();
        }
    }
}

使用此实现,调用 foo(c1, c2) 将正式不对 c1 或 c2 执行任何操作。所以它不满足规定的要求。但是,它将创建一个新的 IntClass 实例,该实例具有 c1 和 c2 的组合行为。 IE。在 c3 上调用 action2() 将在 c1 和 c2 上调用 action2()。我认为这可能是要求应该是的(只是猜测)。

总而言之,这意味着返回的对象 (c3) 将具有任意 IntClass 实现的行为。然而,这并不意味着你可以做到SomeArbitraryImplClass c3 = (SomeArbitraryImplClass) foo(c1, c2);,因为这样做会违反接口抽象的整个概念(肯定不会在面试中提倡)。

解决方案是the Composite Pattern的一个例子。

如果您使用广义抽象,则必须确保它们与您打算用它们解决的问题兼容。由于所描述的问题设置中的所有 actionI() 方法都具有 void 返回类型,因此无法提取任何 PairInterface 实例的最小值和最大值(如您的评论中所述)。抽象不允许这样做。

如果这是您想要做的,则解决方案要简单得多。 (这是基于 PairInterface 包含声明 int getX()int getY() 的假设,这对于 PairInterface 来说似乎是合理的):

public class Test{
    public static PairInterface mix(PairInterface c1, PairInterface c2){
        int minX = Math.min(c1.getX(), c2.getX());
        int maxY = Math.max(c1.getY(), c2.getY());
        return new SomePairInterfaceImplementation(minX, maxY);
    }
}

除此之外,您还必须创建SomePairInterfaceImplementation 类,或者使用一些可以接受x 和y 作为构造函数参数的现有类。

注意:

1) 如果PairInterface 只声明了 void 方法,则不可能计算出 c1 和 c2 的 x 值的最大值或最小值。这些值被封装和隐藏,无法访问。

2) 方法返回的 PairInterface 实现无关紧要。具有接口返回类型的原因是任何一致的接口实现都应该可以工作。

【讨论】:

  • 谢谢,您的解决方案很有趣,虽然不需要。抱歉含糊不清..但这里有一个例子: IntClass 是一个名为:PairInterface .. 实现的类是 ArrayPair 和 SeperatePair .. 第一个有两个元素的 int 数组,而 SeperatePair 有两个 int 变量 x 和y .. 动作函数 .. 可以是 getFirst() 和 getSecond() ... foo() 函数(例如)定义为:
  • public static PairInterface mix(PairInterface c1, PairInterface c2) ,它返回一个新的实例 c3,它具有最小的 x 和最大的 y 例如! - 没有对 c1 和 c2 进行任何更改 ... [所以现在,如何定义 c3? ]
  • 再次为抽象感到抱歉..我是一个算法数学专家..我已经习惯了这种“模糊”的风格:)
【解决方案4】:

您可以使用匿名类创建接口对象并实现所有抽象方法:

IntClass c3 = new IntClass()
{
    @Override
    public void action1()
    {
        // ...
    }

    // ...

    @Override
    public void actionM()
    {
        // ...
    }

};

【讨论】:

  • 这不是我们需要的解决方案。 foo() 的实现必须适用于接口 IntClass 的所有有效实现。也就是说,(据我所知)如果我调用: 1) ImpClass1 s1 = new ImpClass1(); ImplClass2 = 新 ImpClass2(); ImpClass3 s3 = foo(s1, s2) .. 函数必须正常工作!
  • 因为我事先不知道c3是什么类型,所以我只知道它是Interface IntClass的一个实例..因此,我不知道函数action1().. actionM() 将被覆盖 .. 如果我知道 .. 我会简单地将 foo 定义为 public static ImpClassk foo(IntClass c1, IntClass c2) ... [这很容易]
  • @AhmedJedda 如果方法签名是public static IntClass foo(IntClass, IntClass),则不可能调用ImpClass3 s3 = foo(s1, s2); 无论你提供什么实现,调用 foo(...) 总是会导致编译器错误。因此,如果这是您所期望的,我认为您的规范有问题。
  • @AhmedJedda(上一条消息的继续)可以做到ImpClass3 s3 = (ImpClass3) foo(s1, s2);。但是,如果该代码行没有异常地执行,则调用 ImpClass2 s3 = (ImpClass2) foo(s1, s2); 将引发类转换异常。
  • @AhmedJedda 在我看来,“必须适用于 IntClass 的所有实现”的更明智的解释是应该可以将任意 IntClass 的实例作为参数传递给 foo(... ) 方法。
【解决方案5】:

你确定你的问题是正确的吗?来自您对 Eng.Fouad 的评论

ImpClass3 s3 = foo(s1, s2)

这没有意义。右边有抽象的东西,左边有具体的东西是错误的,通常反之亦然。就像你点了“吃什么,随便什么”,但期待的是“可乐巨无霸鸡”:)

【讨论】:

  • 好吧,在那种情况下,他们只会做演员表.. - 但我确信我的问题是正确的.. 它在我身边,我复制粘贴它......如果它适合我.那么我认为这是不可能的!但我无法证明!
  • 是的,他们可以向下转换,但首先他们必须检查返回的值是否真的是 ImpClass3 的实例。这是不好的做法,我怀疑他们是否真的提到过。抱歉,他们实际上试图检查您的 Java 知识的哪一部分仍然没有意义。
  • 有些经理只是想骗你知道你知道还是不知道!这只是愚蠢的!
猜你喜欢
  • 1970-01-01
  • 2011-05-16
  • 1970-01-01
  • 1970-01-01
  • 2016-05-15
  • 2011-06-22
  • 2019-02-10
  • 1970-01-01
  • 2016-01-09
相关资源
最近更新 更多