为Java泛型键入别名

时间:2009-03-25 21:25:14

标签: java generics casting

我在Java中有一套相当复杂的泛型类。例如,我有一个接口

interface Doable<X,Y> {
  X doIt(Y y);
}

和实施

class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> {
  Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... }
}

在实际实施中,Doable有很多方法,因此Foo<Bar<Baz,Qux>>等会一遍又一遍地出现。 (信不信由你,通用类型比这更痛苦。我已经简化了它们的例子。)

我想简化这些,以节省自己的打字和缓解眼睛的压力。我想要的是为Foo<Bar<Baz,Qux>>等提供一个简单的“类型别名”,比如FooBBQFooBZQ

我目前的想法是定义包装类:

class FooBBQ { 
  public static FooBBQ valueOf(Foo<Bar<Baz,Qux>> fooBBQ) { 
    return new FooBBQ(fooBBQ); 
  }
  private Foo<Bar<Baz,Qux>> fooBBQ;
  private FooBBQ(Foo<Bar<Baz,Qux>> fooBBQ) { 
    this.fooBBQ = fooBBQ; 
  }
  public Foo<Bar<Baz,Qux>> toGeneric() {
    return fooBBQ;
  }
}

class FooBZQ { /* pretty much the same... */ }

class DoableImpl implements Doable<FooBBQ,FooBZQ> { 
  FooBBQ doIt(FooBZQ fooBZQ) { ... }
}

这很有效,但它有一些缺点:

  1. 我们需要为每个通用实例定义单独的包装器。包装器类很短且风格化,但我无法找到一种方法来宏观化它们。
  2. 我们有调用valueOftoGeneric以在FooBBQFoo<Bar<Baz,Qux>>之间进行转换的翻译开销(概念上,如果不是操作上)。例如,如果doIt调用某个期望Foo<Bar<Zot,Qux>>的库例程(实际实现的那个),我们最终会得到类似

    的内容。
    return FooBBQ.valueOf( libraryCall( fooBZQ.toGeneric() ) )
    

    我们最初的地方

    return libraryCall(fooBZQ);
    
  3. 是否有其他方法可以获得我想要的“类型别名”行为?也许使用一些第三方宏工具集?或者我是否需要接受我将不得不进行大量的输入,一种方式(使用实现中的泛型类型)或另一种方式(为它们编写包装器)?也许有这么多通用参数飞来飞去只是一个坏主意,我需要重新思考这个问题吗?

    [更新]好的,我禁止任何进一步的“不要那样做”的答案。假设Foo<Bar<Baz,Qux>>在我的问题域中具有真正的价值(Pete Kirkham可能是正确的,它具有足够的值以获得具有描述性名称的正确包装类)。但这是一个编程问题;不要试图解决问题。

5 个答案:

答案 0 :(得分:10)

如果你想要完全的类型安全,我认为如果没有某种包装类,你可以做得更好。但是,为什么不让这些类继承/实现原始的通用版本,如下所示:

public class FooBBQ extends Foo<Bar<Baz,Qux>> {
...
}

这消除了对toGeneric()方法的需要,在我看来,它更清楚,它只是一个类型别名。此外,泛型类型可以在没有编译器警告的情况下强制转换为FooBBQ。如果可能的话,我个人倾向于制作Foo, Bar, Baz...接口,即使在实现中会出现一些代码重复。

现在,在不知道具体问题域的情况下,很难说你是否需要,比如FooBBQ,就像在你的例子中,或者可能是:

public class FooBar<X, Y> extends Foo<Bar<X, Y>> {
...
}

另一方面,您是否考虑过简单地配置Java编译器以不显示某些通用警告,而只是省略通用定义的部分?或者,使用策略性地@SuppressWarnings("unchecked")?换句话说,您是否可以DoableImpl仅“部分通用化”:

class DoableImpl implements Doable<Foo<Bar>>,Foo<Bar>> {
    Foo<Bar> doIt(Foo<Bar> foobar) { ... } 
}

并忽略警告,以减少代码混乱?再说一次,如果没有一个具体的例子,很难做出决定,但这是你可以尝试的另一件事。

答案 1 :(得分:5)

Scala对类型别名有很好的支持。例如:

type FooBBQ = Foo[Bar[Baz,Qux]]

我意识到如果您没有切换到Scala的选项,这个答案将没有用。但如果您确实可以选择切换,那么您可能会有更轻松的时间。

答案 2 :(得分:2)

  

也许有这么多通用参数飞来飞去只是一个坏主意,我需要重新思考这个问题?

很可能。是否需要将8个维度的“Doit”专门化?

在很多情况下,这些类型并不存在于真空中,您应该考虑“包装器”所代表的域对象,而不是将它们用作编码方便。

答案 3 :(得分:1)

嗯,Java没有类型别名,所以你运气不好。但是,类型别名有时可以用类型变量替换!所以我们用更多的泛型解决了太多泛型的问题!

当您从示例中删除所有内容时,我无法猜测引入其他类型变量的位置或是否有意义,但这是一种可能的分解:

class DoableImplFoo<A,B> implements Doable<Foo<A>,Foo<B>> {
  public DoableImplFoo(SomePropertyOf<A,B> aAndBAreGoodEnough) { ... }
  Foo<A> doIt(Foo<B> fooB) { ... }
}

当您将A实例化为Bar<Baz,Qux>B再次Bar<Zot,Qux>时,您可能会发现有一些样板,但它可能比您原来的更少

答案 4 :(得分:0)

我会说,是的,你需要重新思考这个问题。声明

 class DoableImpl implements Doable<Foo<Bar<Baz,Qux>>,Foo<Bar<Zot,Qux>>> {
    Foo<Bar<Baz,Qux>> doIt(Foo<Bar<Zot,Qux>> fooBZQ) { ... } 
 }

是一个非常明显的过度使用泛型的案例。