引用实现两个或多个给定接口的任何类的对象

时间:2013-12-31 17:14:10

标签: java jvm jvm-languages

给定任何接口I,可以声明一个变量,该变量包含对实现C的类I的任何对象的引用:

I i = new C();

我想做类似的事情。给定两个接口,我想声明一个变量,该变量包含对实现两个接口的类的任何对象的引用:

interface Foo { void foo(); }
interface Bar { void bar(); }

class Humpty implements Foo, Bar {
  public void foo() { System.out.println("Humpty.foo()"); }
  public void bar() { System.out.println("Humpty.bar()"); }
}

class Dumpty implements Foo, Bar {
  public void foo() { System.out.println("Dumpty.foo()"); }
  public void bar() { System.out.println("Dumpty.bar()"); }
}

public class Program {
  public static void main(String[] args) {
    // I actually have no idea what the syntax should be.
    Random random = new Random();
    // Fix: I previously used <? extends Foo, Bar>, thanks Jon Skeet and vijucat
    <? extends Foo & Bar> foobar;
    if (random.nextBoolean())
      foobar = new Humpty();
    else
      foobar = new Dumpty();
    foobar.foo();
    foobar.bar();
  }
}

我已尝试过上述代码段,但<? extends Foo, Bar>会导致编译错误。正确的语法应该是什么?我想知道其他静态类型的JVM语言是否可行:Scala,Kotlin,Ceylon等。

5 个答案:

答案 0 :(得分:3)

如果你不介意压制臭名昭着的“未经检查的演员”警告,这是一个“解决方案”:

@SuppressWarnings("unchecked")
public class Program {
  public static<FooBar extends Foo & Bar> void main(String[] args) {
    // Note the explicit cast needed
    FooBar foobar = (FooBar) new Humpty();
    foobar.foo();
    foobar.bar();

    // Note the explicit cast needed
    foobar = (FooBar) new Dumpty();
    foobar.foo();
    foobar.bar();
  }
}

另见Jon Skeet的答案,该答案适用于优雅可能的情况。

老实说,如果没有这么多角落案件,这应该是可能的。 Java Generics的实现非常混乱,不理解它们可能表明你知道如何明智地分配你的时间,而不是你不是技术领域的专家! : - )

答案 1 :(得分:2)

  

给定两个接口,我想声明一个变量,该变量包含对实现两个接口的类的任何对象的引用

不幸的是你做不到。您可以对泛型方法中的参数执行此操作,如下所示:

public static <T extends Foo & Bar> void someMethod(T value) {
    Foo x = value;
    Bar y = value;
}

...或者你也可以为泛型类中的实例变量做同样的事情:

class Test<T extends Foo & Bar> {

    private T value;

    public Test(T value) {
        this.value = value;
    }
}

...但你不能声明一个只需要满足这两个约束的变量。

(注意这里有两个约束的语法 - 它是&而不是逗号。)

答案 2 :(得分:1)

你可以直接施放物体。你可以这样做:

Humpty foobar = new Humpty();
((Foo)foobar).foo();
((Bar)foobar).bar();

答案 3 :(得分:0)

您可以创建接口层次结构,但这实际上仅适用于相关接口。如果Bar和Foo完全不相关,这将不起作用。

interface Foo { void foo(); }
interface Bar extends Foo { void bar(); } //implementations of Bar will need to implement both foo() and bar()

Humpty和Dumpty只需要实现Bar,然后就可以创建一个对象:

<? extends Foo> foobar = new Humpty();

答案 4 :(得分:0)

这不是很好,但它有效,它是安全的(modulo null引用),并且它不需要抑制任何警告。 Jon Skeet实际上朝着正确的方向前进,但在中间停了下来:

/**
 * Existentially quantify over things that implement Foo and Bar.
 * 
 *   FooBar = \exists (T extends Foo & Bar). T
 */
interface FooBar extends Foo, Bar {}

/**
 * Universally quantify over things that implement Foo and Bar.
 * 
 *   \forall (T extends Foo & Bar). T --> WrappedFooBar<T>
 *   \forall (T extends Foo & Bar). WrappedFooBar<T> --> FooBar
 */
class WrappedFooBar<T extends Foo & Bar> implements FooBar {
  private T wrapped;
  public WrappedFooBar(T wrapped) { this.wrapped = wrapped; }
  public void foo() { this.wrapped.foo(); }
  public void bar() { this.wrapped.bar(); }
}

public class Program {
  public static void main(String[] args) {
    Random random = new Random();
    FooBar foobar;
    if (random.nextBoolean())
      foobar = new WrappedFooBar(new Humpty());
    else
      foobar = new WrappedFooBar(new Dumpty());
    foobar.foo();
    foobar.bar();
  }
}

主要的警告是,这个解决方案显然无法扩展:人们可能需要编写大量的包装器。