Java 泛型的类型别名

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/683533/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-29 13:22:22  来源:igfitidea点击:

Type aliases for Java generics

javagenericscasting

提问by Chris Conway

I have a fairly complicated set of generic classes in Java. For example, I have an interface

我有一组相当复杂的 Java 泛型类。例如,我有一个界面

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

and the implementation

和实施

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

In the real implementation, Doablehas quite a few methods and so Foo<Bar<Baz,Qux>>, etc., appear over and over again. (Believe it or not, the generic types are quite a bit more painful than this. I've simplified them for the example.)

在实际的实现中,Doable有不少方法Foo<Bar<Baz,Qux>>等等,一遍遍地出现。(信不信由你,泛型类型比这要痛苦得多。我已经为示例简化了它们。)

I'd like to simplify these, to save myself typing and to ease the strain on my eyes. What I'd like is to have a simple "type alias" for Foo<Bar<Baz,Qux>>, etc., say FooBBQand FooBZQ.

我想简化这些,以节省自己的打字时间并减轻我的眼睛疲劳。我想要的是有一个简单的“类型别名” Foo<Bar<Baz,Qux>>,例如FooBBQFooBZQ

My current idea is to define wrapper classes:

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

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) { ... }
}

This works well, but it has a few drawbacks:

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

  1. We need to define separate wrappers for each generic instance. The wrapper classes are short and stylized, but I can't figure out a way to macro-ize them.
  2. We have the translation overhead (conceptually, if not operationally) of calling valueOfand toGenericto convert between FooBBQand Foo<Bar<Baz,Qux>>. For example, if doItcalls into some library routine that expects a Foo<Bar<Zot,Qux>>(which the real implementation does), we end up with something like

    return FooBBQ.valueOf( libraryCall( fooBZQ.toGeneric() ) )
    

    where we would originally have had

    return libraryCall(fooBZQ);
    
  1. 我们需要为每个通用实例定义单独的包装器。包装类很短而且风格化,但我想不出一种方法来宏化它们。
  2. 我们有打电话的转换开销(概念,如果没有操作性)valueOftoGeneric之间进行转换FooBBQFoo<Bar<Baz,Qux>>。例如,如果doIt调用一些需要 a Foo<Bar<Zot,Qux>>(实际实现是这样的)的库例程,我们最终会得到类似的结果

    return FooBBQ.valueOf( libraryCall( fooBZQ.toGeneric() ) )
    

    我们原本应该有的地方

    return libraryCall(fooBZQ);
    

Is there some other way to get the "type alias" behavior I want here? Perhaps using some third-party macro toolset? Or do I need to accept that I'm going to have to do a lot of typing, one way (using the generic types in the implementation) or the other (writing wrappers for them)? Maybe having this many generic parameters flying around is just a bad idea and I need to re-think the problem?

有没有其他方法可以获得我想要的“类型别名”行为?也许使用一些第三方宏工具集?或者我是否需要接受我将不得不进行大量打字,一种方式(在实现中使用泛型类型)或另一种方式(为它们编写包装器)?也许让这么多泛型参数四处乱飞只是一个坏主意,我需要重新考虑这个问题?

[UPDATE] OK, I'm banning any further "don't do that" answers. Take it as a given that Foo<Bar<Baz,Qux>>has genuine value in my problem domain (Pete Kirkham may be right that it has enoughvalue to get a proper wrapper class with a descriptive name). But this is a programming problem; don't try to define the problem away.

[更新] 好的,我禁止任何进一步的“不要那样做”的回答。将其视为Foo<Bar<Baz,Qux>>在我的问题域中具有真正价值的给定(Pete Kirkham 可能是对的,它具有足够的价值来获得具有描述性名称的适当包装类)。但这是一个编程问题;不要试图定义问题。

回答by javashlook

If you want full type safety, I don't think you can do better without some kind of wrapper classes. But, why not make those classes inherit/implement the original generic versions, like this:

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

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

This eliminates the need for toGeneric()method, and it is more clear, in my opinion, that it is just a type alias. Also, generic type can be cast into FooBBQwithout a compiler warning. It would be my personal preference to make Foo, Bar, Baz...interfaces, if possible, even if some code duplication would occur in implementation.

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

Now, without knowing concrete problem domain, it is hard to say whether you need, say FooBBQ, like in your example, or perhaps a:

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

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

On the other hand, have you thought about simply configuring Java compiler not to show some of the generic warnings, and simply omit the parts of generic definition? Or, use strategically placed @SuppressWarnings("unchecked")? In other words, can you make DoableImplonly "partly genericized":

另一方面,你有没有想过简单地配置 Java 编译器不显示一些通用警告,并简单地省略通用定义的部分?或者,战略性地使用@SuppressWarnings("unchecked")? 换句话说,你能不能DoableImpl只“部分泛化”:

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

and ignore the warnings for the sake of less code clutter? Again, hard to decide without a concrete example, but it is yet another thing you can try.

为了减少代码混乱而忽略警告?同样,如果没有具体的例子就很难决定,但这是你可以尝试的另一件事。

回答by Jesse Hallett

Scala has nice support for type aliases. For example:

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

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

I realize that this answer won't be helpful if you don't have the option of switching to Scala. But if you do have the option of switching you might have an easier time.

我意识到如果您没有切换到 Scala 的选项,这个答案将无济于事。但是,如果您确实可以选择切换,则可能会更轻松。

回答by Pete Kirkham

Maybe having this many generic parameters flying around is just a bad idea and I need to re-think the problem?

也许让这么多泛型参数四处乱飞只是一个坏主意,我需要重新考虑这个问题?

Very probably. Do need to specialise 'Doit' in 8 dimensions?

很有可能。是否需要专门研究 8 个维度的“Doit”?

In a lot of cases, these types don't exist in a vacuum and you should be thinking what domain objects your 'wrapper' represents rather than using them as a coding convenience.

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

回答by Rotsor

Well, Java has no type aliases so you're out of luck. However, type aliases sometimes can be replaced with type variables! So we solve the problem of too many generics with even more generics!

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

As you've stripped all content from your example I can't guess where or whether it makes sense to introduce additional type variables, but here is one possible decomposition:

由于您从示例中删除了所有内容,因此我无法猜测在何处引入其他类型变量或是否有意义,但这是一种可能的分解:

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

When you instantiate Ato Bar<Baz,Qux>and Bto Bar<Zot,Qux>later you may find that there is some boilerplate again, but it could be less than what you originally had.

当您稍后实例化AtoBar<Baz,Qux>Bto 时,Bar<Zot,Qux>您可能会发现再次有一些样板,但它可能比您最初拥有的要少。

回答by JesperE

I would say that, yes, you need to rethink the problem. The declaration

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

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

is a pretty clear case of overusing generics.

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