【问题标题】:How should I cast for Java generic with multiple bounds?我应该如何转换具有多个边界的 Java 泛型?
【发布时间】:2008-11-25 17:29:28
【问题描述】:

是否可以将 Java 中的对象转换为组合的泛型类型?

我有一个类似的方法:

public static <T extends Foo & Bar> void doSomething(T object) {
    //do stuff
}

如果我有一个实现两个接口(Foo & Bar)的类,调用这个方法是没有问题的。

问题是当我需要调用这个方法时,我需要传递给它的对象被接收为java.lang.Object,我需要转换它以使编译器满意。但我不知道如何制作这个演员表。

编辑:

问题在于这样的函数:

public void problemFunction (Object o) {
  if ( o instanceof Foo && o instanceof Bar) {
      doSomething((Problematic cast) o);
  }
}

}

【问题讨论】:

    标签: java generics casting polymorphism


    【解决方案1】:

    Java 8 引入了casting with additional bounds 的可能性。您可以将Object 转换为带有多个interfacesclass(或仅作为多个interfaces)。

    所以这个:

    doSomething((Problematic cast) o);
    

    就变成这样了:

    doSomething((Foo & Bar) o);
    

    【讨论】:

      【解决方案2】:

      很遗憾,您无法通过合法的演员阵容来满足这种情况。必须有一个已知的类型来实现您需要作为边界的所有接口,以便您可以强制转换为它。可能是您为此目的创建的类型,也可能是某个现有类型。

      interface Baz extends Foo, Bar { }
      
      public void caller(Object w) {
        doSomething((Baz) w);
      }
      

      如果已知其他类型(例如 Baz)来满足界限,您可以测试这些类型,并在调用者中创建一个分支来调用 doSomething 并转换为这些类型。不好看。

      您也可以使用委托,创建自己的类Baz,满足doSomething 要求的界限。然后将传递的对象包装在 Baz 类的实例中,并将该包装器传递给 doSomething

      private static class FooBarAdapter implements Foo, Bar {
        private final Object adaptee;
        FooBarAdapter(Object o) {
          adaptee = (Foo) (Bar) o;
        }
        public int flip() { return ((Foo) adaptee).flip(); }
        public void flop(int x) { ((Foo) adaptee).flop(x); }
        public void blort() { ((Bar) adaptee).blort(); }
      }
      
      public void problemFunction (Object o) {
        doSomething(new FooBarAdapter(o));
      }
      

      【讨论】:

      • 好主意。我以稍微不同的方式使用它。我更喜欢 FooBarAdapter 来获取具有 2 种不同类型的对象。 == 可用于检查它是否是同一个对象(不是必须的)。这样,它们就是编译时类型检查。
      • 我将这种委托模式匿名/命名内部方法类组合到“强制转换”一个我知道的对象,以扩展/实现一组类和接口以表示绑定要求。请参阅乔恩的回答:stackoverflow.com/a/9514406/910718
      • 这似乎是 Java 语言的限制,因为在同一个 JVM 上运行的 Scala 中,这很容易 o.asInstanceOf[Foo with Bar]
      【解决方案3】:
      public static <T extends Foo & Bar> void doSomething(T object)
      

      这似乎表明您将对相关对象执行多个操作。

      我认为,如果您对对象执行的所需操作足够不同,可以跨接口分离,那么它们就足够不同,值得拥有自己的方法。

      您很可能可以重组此代码以调用单独的方法来执行所需的操作。从客户的角度来看,这最终可能会使整个操作更加清晰。

      代替:

      public void problemFunction (Object o) {
        if (o instanceof Foo && o instanceof Bar) {
            doSomething((Problematic cast) o);
        }
      }
      

      变成:

      public void problemFunction(Object o) {
        if (o instanceof Foo && o instanceof Bar) {
            fooifySomething((Foo) o);
            baratizeSomething((Bar) o);
        }
      }
      

      【讨论】:

      • LMAO fooify,这对我来说是新的 xD
      【解决方案4】:

      通过将联合类型编码为方法的类型参数的可怕方式,可以“向上转换”为联合类型;对于您的示例,您可以编写

      private <Q extends Foo & Bar> Q upcast(final Object in) {
          return (Q) in;
      }
      
      // ... elsewhere...
      
      if (myObject instanceof Foo && myObject instanceof Bar) {
          doSomething(upcast(myObject));
      }
      

      【讨论】:

      • 谢谢。唯一令人烦恼的是它会生成未经检查的演员表。但是,我使用了一个修改版本,它采用 Foo 而不是 Object 并检查它是否实现 Bar,然后像您所做的那样返回转换,否则会抛出 ClassCastException。
      【解决方案5】:

      作为一种解决方法,您可以定义另一个接口 FooBar 来扩展 Foo 和 Bar 并让您的类实现它。它甚至不必是顶级接口 - 如果您不想弄乱或改进其余代码,可以将其声明为私有:

      private interface FooBar extends Foo, Bar {}
      public void problemFunction (Object o) {
        if ( o instanceof Foo && o instanceof Bar) {
            doSomething((FooBar) o);
        }
      }
      

      【讨论】:

      • 我相信这行不通,因为不能保证 o 实际上有“实现 FooBar”。我不认为 Java 会假设它。
      • @ArtB 答案是“让你的班级实现它。”。
      猜你喜欢
      • 2015-09-29
      • 2016-04-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-02-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多