Java枚举和泛型 [英] Java enums and generics

查看:164
本文介绍了Java枚举和泛型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个事情让我烦恼了一阵子。我已经询问问题之前,但可能有一个坏的措辞和一个太抽象的例子。所以不清楚我实际在问什么。我会再尝试。请不要跳过结论。我希望这个问题不容易回答!



为什么我不能在Java中有一个泛型类型参数的枚举?



问题不在于为什么不可能在语法上。我知道只是不支持。问题是:JSR为什么忘记或省略这个非常有用的功能?我无法想象与编译器有关的原因,为什么不可行。



这是我最想做的事情。这在Java中是可能的。这是Java 1.4创建类型安全枚举的方法:

  // SQL数据类型的模型类及其与Java类型的映射
public class DataType< T>实现Serializable,Comparable< DataType< T> {
private final String name;
私人最终类别< T>类型;

public static final DataType< Integer> INT = new DataType< Integer>(int,Integer.class);
public static final DataType< Integer> INT4 = new DataType< Integer>(int4,Integer.class);
public static final DataType< Integer> INTEGER = new DataType&Integer>(integer,Integer.class);
public static final DataType< Long> BIGINT = new DataType< Long> (bigint,Long.class);

private DataType(String name,Class&T; type){
this.name = name;
this.type = type;
}

//返回T.我发现这常常非常有用!
public T parse(String string)throws异常{
// [...]
}

//检查出来。高级泛型:
public T [] parseArray(String string)throws异常{
// [...]
}

//甚至更高级:
public DataType< T []> getArrayType(){
// [...]
}

// [...更多方法...]
}

然后,您可以使用许多其他的< T> 地方

  public class Utility {

//通用方法...
public static < T> T doStuff(DataType< T> type){
// [...]
}
}

但这些东西是不可能的枚举:

  //这不能要完成
public enum DataType< T> {

//这两个都不可以...
INT< Integer>(int,Integer.class),
INT4< Integer>(int4课程)

// [...]
}

现在,正如我所说。我知道这些东西都是这样设计的。 enum 是语法糖。仿制药也是如此。实际上,编译器完成所有的工作,并将枚举转换成 java.lang.Enum 的子类,并将其转换为转换合成方法。



但是为什么编译器不能继续进行并允许通用枚举?



编辑
这是我期望的编译器生成的Java代码:

  public class DataType< T>扩展枚举< DataType<?>> {
// [...]
}


解决方案

我想猜一下,这是因为Enum类本身的类型参数的协方差问题,它被定义为 Enum< E extends Enum&E; ;> ,尽管调查所有这些角落的情况有一点点。



除此之外,枚举的主要用例是与EnumSet和valueOf这样的东西,你有一个不同的通用参数的东西的集合,并从一个字符串获取值,所有这些都不会支持或更糟的枚举本身的通用参数。



我知道我总是在一个痛苦的世界,当我试图得到这个想法与泛型,我想象的语言设计师偷看这个深渊,决定不去那里,特别是因为功能是同时发展,这意味着Enum方面的更多的不确定性。



或者换个方法,我在处理自己具有通用参数的类时,将会遇到 Class< T> 的所有问题,您必须对原始类型进行大量的转换和处理。语言设计师觉得不是真正的东西值得你看的用例类型。



编辑:响应评论(和汤姆 - downvote?),嵌套泛型参数使各种坏事发生。枚举实现可比较。如果使用泛型表达式,那么客户端代码中的枚举的两个任意元素根本无效。一旦处理了Generic参数的Generic参数,您就会遇到各种边界问题和头痛。很难设计一个处理好的类。在可比较的情况下,我无法找出一种方法来比较枚举的两个任意成员,而不返回原始类型并获得编译器警告。可以吗?



其实上面是尴尬的错误,因为我在问题中使用DataType作为我的模板考虑这个,但实际上是枚举将有一个子类,所以这是不正确的。



然而,我坚持我的答案的要点。汤姆提出了 EnumSet.complementOf ,当然我们还有 valueOf 会产生问题,而且设计的程度的枚举可以工作,我们必须意识到这是20/20事后的事情。 Enum与仿制药同时设计,没有验证所有这些角落的好处。特别是考虑到具有通用参数的枚举的用例相当有限。 (但是再次,EnumSet的用例也是如此)。


This thing is troubling me for a while now. I have asked questions before, but probably with a bad phrasing and an example that was too abstract. So it wasn't clear what I was actually asking. I'll try again. And please don't jump to conclusions. I expect that the question is not easy at all to answer!

why can't I have an enum with generic type parameters in Java?

The question is not about why it's not possible, syntactically. I know it's just not supported. The question is: why did the JSR people "forget" or "omit" this very useful feature? I can't imagine a compiler-related reason, why it wouldn't be feasible.

Here's what I would love to do. This is possible in Java. It's the Java 1.4 way to create typesafe enums:

// A model class for SQL data types and their mapping to Java types
public class DataType<T> implements Serializable, Comparable<DataType<T>> {
    private final String name;
    private final Class<T> type;

    public static final DataType<Integer> INT      = new DataType<Integer>("int", Integer.class);
    public static final DataType<Integer> INT4     = new DataType<Integer>("int4", Integer.class);
    public static final DataType<Integer> INTEGER  = new DataType<Integer>("integer", Integer.class);
    public static final DataType<Long>    BIGINT   = new DataType<Long>   ("bigint", Long.class);    

    private DataType(String name, Class<T> type) {
        this.name = name;
        this.type = type;
    }

    // Returns T. I find this often very useful!
    public T parse(String string) throws Exception {
        // [...]
    }

    // Check this out. Advanced generics:
    public T[] parseArray(String string) throws Exception {
        // [...]
    }

    // Even more advanced:
    public DataType<T[]> getArrayType() {
        // [...]
    }

    // [ ... more methods ... ]
}

And then, you could use <T> in many other places

public class Utility {

    // Generic methods...
    public static <T> T doStuff(DataType<T> type) {
        // [...]
    }
}

But these things are not possible with an enum:

// This can't be done
public enum DataType<T> {

    // Neither can this...
    INT<Integer>("int", Integer.class), 
    INT4<Integer>("int4", Integer.class), 

    // [...]
}

Now, as I said. I know these things have been designed exactly that way. enum is syntactic sugar. So are generics. Actually, the compiler does all the work and transforms enums into subclasses of java.lang.Enum and generics into casts and synthetic methods.

but why can't the compiler go further and allow for generic enums??

EDIT: This is what I would expect as compiler-generated Java code:

public class DataType<T> extends Enum<DataType<?>> {
    // [...]
}

解决方案

I'm going to guess a bit and say that it is because of covariance issues on the type parameter of the Enum class itself, which is defined as Enum<E extends Enum<E>>, although it is a bit much to investigate all the corner cases of that.

Besides that, a primary use case of enums is with things like EnumSet and valueOf where you have a collection of things with different generic parameters and get the value from a string, all of which would not support or worse the generic parameter on the enum itself.

I know I'm always in a world of pain when I try to get that fancy with Generics, and I imagine the language designers peeked at that abyss and decided to not go there, especially since the features were developed concurrently, which would mean even more uncertainty for the Enum side of things.

Or put another way, it would have all the problems of Class<T> in dealing with classes which themselves have generic parameters, and you would have to do a lot of casting and dealing with raw types. Not truly something that the language designers felt was worth it for the type of use case you are looking at.

EDIT: In response to the comments (and Tom - a downvote?), nested generic parameter makes all kinds of bad things happen. Enum implements Comparable. That simply would not work to compare two arbitrary elements of the enum in client code if generics were in play. Once you deal with a Generic parameter of a Generic parameter, you end up with all kinds of bounds problems and headaches. It is hard to design a class that handles it well. In the case of comparable, I could not figure out a way to make it work to compare two arbitrary members of an enum without reverting to raw types and getting a compiler warning. Could you?

Actually the above is embarrassingly wrong, as I was using the DataType in the question as my template for thinking about this, but in fact an Enum would have a subclass, so that isn't quite right.

However, I stand by the gist of my answer. Tom brought up EnumSet.complementOf and of course we still have valueOf that produces problems, and to the degree that the design of Enum could have worked, we have to realize that that is a 20/20 hindsight thing. Enum was being designed concurrently with generics and didn't have the benefit of validating all such corner cases. Especially considering that the use case for an Enum with a generic parameter is rather limited. (But then again, so is the use case for EnumSet).

这篇关于Java枚举和泛型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆