将字节转换为枚举类型

4

I have the following code:


public enum PortableTypes { Boolean, etc...};

public void testConversion() {
  byte b = 5;
  PortableTypes type = (PortableTypes)b;
}

当我编译时,提示我这些是无法转换的类型。我不知道像这样简单的东西会被Java忽略。我是否遗漏了什么,或者Java根本不支持将类型转换为枚举?我还测试了一个int,但它也失败了。
如果不支持,最简单的解决方法是什么?
编辑:
由于每个人都在抱怨“糟糕的设计”,所以我会进一步解释一下。我正在使用枚举来表示您可以使用我的TcpServer发送的类型。数据包带有此类型,并且在接收端,我查看该类型并将其用于将从数据包中读取的字节转换为可用数据。显然,您无法使用OutputStream发送枚举,因此我需要将枚举值转换为数字值以进行发送。我不同意这是“糟糕的设计”的想法,我认为枚举确实应该用于此目的,但是这种设计在Java中不起作用,因为它具有强类型。

1
“Something as simple as this”... 抱歉,这是什么? - corsiKa
@glowcoder 这只是一个从字节到枚举的简单转换。因此,在我的示例中,您将获取枚举中的第6个条目。 - Darkhydro
1
@Darkhydro 为什么你不能使用outputstream发送一个枚举?如果你使用objectoutputstream,你可以像发送其他任何对象一样发送它。 - corsiKa
我从Java套接字编程中学到的一课是:“如果您没有使用序列化来发送消息,那么最好有一个非常好的理由。” - corsiKa
如果你需要将枚举转换为字节或整数,那么你可能没有正确使用它们...至少在Java世界中。 - user489041
@glowcoder,我现在正在尝试使用ObjectOutputStream来实现它。如果您发布一个答案,我会将您的标记为解决方案,因为这是迄今为止我看到的最简单(也是最强大)的答案。 - Darkhydro
8个回答

7
您可能认为enum就像在C/C++或C#中声明常量的语法糖一样。然而,Java的enum是完全不同的东西......它是类型安全枚举模式的内置实现。由于它是强类型的,所以不能直接从字节或甚至整数转换为枚举。
但是,在Java中,所有枚举都是完整的类,因此您可以实现一个额外的方法来为您执行转换。
希望能对您有所帮助。 编辑:再次说明,我不是Java程序员,但我想这样做是可行的:
public enum PortableTypes {
    Boolean,
    Type2,
    Type3;

    public static PortableTypes convert(byte value) {
        return PortableTypes.values()[value];
    }        
};

public void testConversion() {
    byte b = 5;
    PortableTypes type = PortableTypes.convert(b);
}

请告诉我它是否有效。


那么有没有解决方法?还是我必须声明一堆静态常量整数…? - Darkhydro

4

Java枚举类型有序号来帮助您。

要从常量获取数字,请调用WHATEVER.ordinal()。它返回声明顺序中常量的索引。例如:

enum Underwear { BRIEFS, BOXERS };

Underwear.BRIEFS.ordinal() 的值为0,Underwear.BOXERS.ordinal() 的值为1。

要从数字获取枚举对象,请调用 values() 并索引返回的数组。

具体信息请参考官方文档:http://download.oracle.com/javase/6/docs/api/java/lang/Enum.html


什么是ordinal(),它的作用是什么?WHATEVER的类型是什么?我在枚举或常量中没有看到这个方法。 - Darkhydro
Java在每个枚举项上都提供了一个.ordinal()方法。如果您向枚举中添加更多方法(如其他答案所述),它们也会出现在同一位置。 - bmargulies
@Darkhydro:.ordinal() 简单来说就是枚举类型在声明时的顺序号。而另一种方向可以使用 EnumType.values()[i] - Paŭlo Ebermann

2

或者写一个静态getter方法:

PortableTypes.get(b);

public static PortableTypes get(byte b) {
  return PortableTypes.values()[b];
}

对于 PortableTypes.values()[b] 的替代方法有点啰嗦。 - bmargulies
冗长了一些,但更面向对象,不是吗? - Beez
好的,我们可以整天辩论这个问题。接受观点。毕竟他也可以在他的本地C#中进行相同类型的面向对象封装。 - bmargulies

2

枚举是一种类型(尽管在持久性等方面通常默认为int值),它实际上具有一种类型。更重要的问题是,为什么你想将其强制转换,我通常会将其归类为“不良实践”。强类型是现代语言的主要优势。

如果你只需要一个字节值,请查阅Java中的枚举设计模式。

如果你需要该值,可以执行以下操作:

enum SomeEnum
{
    a((byte)0x01),
    b((byte)0x02),
    c((byte)0x03),
    d((byte)0x04);

    byte byteVal;

    SomeEnum(byte b)
    {
      byteVal = b;
    }

    //Continued long winded example
    public MyNum replacementValueOf(byte b)
    {
      for(MyNum num : MyNum.values())
        if(b == num.getByte())
          return num;
      throw new RuntimeException("Your byte " + b + " was not a backing value for MyNum.");
    }
}

我本来想给出详细的答案,但是我同意上面的帖子,重新思考设计模式。


一种冗长的替代方案,用于ordinal()。 - bmargulies
@Daniel,我如何使用这个新的枚举类型重写我的问题中的示例代码? - Darkhydro
@bmargulies 序数被弃用,因为声明的顺序会破坏代码(悄无声息地)。 - corsiKa
就像在C和C++中一样。如果你的人生目标是给从0开始的数字赋予名称,那也完全没问题。 - bmargulies
在阅读了您上面的评论之后,我认为我上面正在做的是最具可维护性的设计。 您需要确保数据包在两侧使用相同的信息,并且保持静态不变。虽然使用valueOf(int i)可以通过 a&0x000000FF 来获取字节,但如果您有超过0xFF状态或更改其顺序,则可能会出现问题。或者,如果这都是运行时的,只需(byte)(Enum.VALUE.ordinal() & 0xFF)->发送到服务器,然后用Enum.valueOf((int)(packetByte & 0xFF)来返回。 - Daniel B. Chapman
显示剩余2条评论

2
您最有效的解决方案是使用 ObjectOutputStream 互相传递消息。这有许多好处:
  1. 内置 enum 序列化
  2. 更复杂的消息传递扩展性更强
  3. 还可以轻松地传递回调和参数!

感谢您发布这篇文章。绝对是最好的解决方案。这个类真的很强大,很高兴你向我介绍了它。 - Darkhydro
@Darkhydro 没问题。三周前我也遇到了同样的问题,试图通过套接字传递字符串、字节和各种东西。每个消息类型都不同,我有各种开关情况。现在我有一个抽象的“Message”类,我只需要执行Message m = in.readObject(); m.performCallback(engine);`就可以了。它就可以工作了。直接使用,没有花哨的设置或其他东西。 - corsiKa
@Darkhydro,建议你阅读一下https://dev59.com/bm035IYBdhLWcg3wYe8F 这个问题是我提出的,涉及了一些基础知识,对我来说非常有启发性。 - corsiKa
是的,这很有道理。它非常酷,因为它如此容易使用,只需要三行代码。而且它可以与多态一起使用,所以我可以为基础对象的所有子类拥有相同的方法,并且它会根据实际读取的类选择正确的方法。 - Darkhydro
@Darkhydro,你说得完全正确。我的问题最初是关于“如何确定类型?”的,答案是“只要你知道它的父类型(你发送的唯一类型),你就不需要知道它的真实类型。”那天我是一个快乐的开发者。 :) - corsiKa

1
你可以尝试为枚举类型编写一个接受字节参数的方法。它可能看起来像这样:
public enum PortableTypes { 
    Boolean, //etc....
    Int;


    public statc PortableTypes fromByte(byte b) {

        for(PortableTypes t : values())
        {
            if(t.ordinal() == (int)b)
                return t;
        } 
        return null;  //or throw exception
    }
}

我现在手头没有集成开发环境,但我相信类似这样的代码可以工作。希望这能帮到你。


PortableTypes.java:10: ordinal 在 java.lang.Enum 中具有私有访问权限 如果(t.ordinal == (int)b),则会执行它。 - user unknown
谢谢,已修复。虽然我应该提醒一下,不要使用这个,因为已经有更好的解决方案发布了哈哈。 - Chad La Guardia
你的 fromByte 方法应该是 静态的 - Jesper

1

我不确定这样做是否是一个好主意。 你可以使用 PortableTypes.values()[b],但重新思考设计会更好。


@Patrick 真的吗?我在 C# 中经常这样做。为什么会有问题呢?枚举就像是一组带有自己类型的静态整数集合。我这样做的原因是我需要将类型作为 int 发送。 - Darkhydro
1
@Darkhydro:看看我的回答——我也是C#程序员,但据我所知,Java的枚举实际上比C#的实现要优秀得多 - rsenna
values()和ordinal()有什么问题吗?这就是它们存在的原因。 - bmargulies
@bmargulies ordinal()是什么,它的作用是什么? - Darkhydro
@Darkhydro:“枚举就像是一组具有自己类型的静态常量整数集合。” - 在C#中是这样的。但在Java中不是。 - Jon Skeet
@Darkhydro:“枚举就像是一组静态常量整数的集合”。最终,所有这些都只是内存中的0和1的集合。但数据类型的发明正是为了防止这种混淆。 - Patrick

0

只有当类型实际上是派生类型时,才能从超类型强制转换为派生类型。枚举不是 Byte 的类型,反之亦然。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接