无法使用UnsupportedOperationException的默认实现覆盖枚举方法:特性还是错误?

huangapple go评论68阅读模式
英文:

Feature or bug: unable to override enum method with default implementation of UnsupportedOperationException

问题

我有一个应用程序,其中使用枚举来表示选项。其中一些选项应该能够具有一些状态:一个整数值或一个字符串值。(我知道一般情况下枚举应该只有final属性,但在某些情况下,属性的值只有在运行时才知道,并且你不能在运行时为枚举的构造函数传递参数。)我希望这些选项的默认getter和setter的实现是通过抛出UnsupportedOperationException来实现的,就像这样(原始代码的一部分):

public enum FileTreeOption1 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH{
    @Override
    public void setIntValue(int value){
      this.intValue = value;
    }
    @Override
    public int getIntValue(){
      return intValue;
    }
  };

  private int intValue;

  public void setIntValue(int value) {
      throw new UnsupportedOperationException("setIntValue not available for " + this.name());
  }
  public int getIntValue() {
      throw new UnsupportedOperationException("getIntValue not available for " + this.name());
  }
}

然而,当我尝试编译这个代码时,我得到了这些错误消息(在Java 11中):

FileTreeOption1.java:7: error: intValue has private access in FileTreeOption1
      this.intValue = value;
          ^
FileTreeOption1.java:11: error: non-static variable intValue cannot be referenced from a static context
      return intValue;
             ^
2 errors

特别是第二个错误是一个奇怪的错误:没有静态上下文。

作为一种解决方法,我已经成功使我的代码按照下面所示的方式工作。但我的问题是:上面的代码不应该能够编译吗?

解决方法:

public enum FileTreeOption2 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH(true);

  private boolean useIntValue;
  private int intValue;

  FileTreeOption2(boolean useIntValue){
    this.useIntValue = useIntValue;
  }
  FileTreeOption2(){}


  public void setIntValue(int value) {
    if(useIntValue){
      this.intValue = value;
    } else {
      throw new UnsupportedOperationException("setIntValue not available for " + this.name());
    }
  }
  public int getIntValue() {
    if(useIntValue){
      return this.intValue;
    } else {
      throw new UnsupportedOperationException("getIntValue not available for " + this.name());
    }
  }
}
英文:

I have an application that uses an enum for the options. Some of the options should be able to have some state: an int value or a String value. (I know that in general an enum should only have final attributes, but there are use cases in which the value of the attribute is only known at runtime, and you can't pass arguments for the constructor of an enum at runtime.) I would like the default implementation of the getters and setters for these options to be implemented by throwing a UnsupportedOperationException, like this (part of the original code):

public enum FileTreeOption1 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH{
    @Override
    public void setIntValue(int value){
      this.intValue = value;
    }
    @Override
    public int getIntValue(){
      return intValue;
    }
  };

  private int intValue;

  public void setIntValue(int value) {
      throw new UnsupportedOperationException("setIntValue not available for " + this.name());
  }
  public int getIntValue() {
      throw new UnsupportedOperationException("getIntValue not available for " + this.name());
  }
}

However, when I try to compile this, I get these error messages (in java 11):

FileTreeOption1.java:7: error: intValue has private access in FileTreeOption1
      this.intValue = value;
          ^
FileTreeOption1.java:11: error: non-static variable intValue cannot be referenced from a static context
      return intValue;
             ^
2 errors

Especially the second error is a strange one: there is no static context.

As a work around, I have managed to get my code working as shown below. But my question is: shouldn't the code above be able to compile?

Work around:

public enum FileTreeOption2 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH(true);

  private boolean useIntValue;
  private int intValue;

  FileTreeOption2( boolean useIntValue){
    this.useIntValue = useIntValue;
  }
  FileTreeOption2(){}


  public void setIntValue(int value) {
    if(useIntValue){
      this.intValue = value;
    } else {
      throw new UnsupportedOperationException("setIntValue not available for " + this.name());
    }
  }
  public int getIntValue() {
    if(useIntValue){
      return this.intValue;
    } else {
      throw new UnsupportedOperationException("getIntValue not available for " + this.name());
    }
  }
}

答案1

得分: 2

多亏了尼古拉的帮助,我刚刚发现不仅可以覆盖枚举常量中的方法,还可以向其中添加变量。所以这个实际上是有效的:

public enum FileTreeOption1 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH{
	 private int intValue; // 仅 DEPTH 可用的属性
    @Override
    public void setIntValue(int value){
      this.intValue = value;
    }
    @Override
    public int getIntValue(){
      return intValue;
    }
  };

  public void setIntValue(int value) {
      throw new UnsupportedOperationException("setIntValue 对于 " + this.name() + " 不可用");
  }
  public int getIntValue() {
      throw new UnsupportedOperationException("getIntValue 对于 " + this.name() + " 不可用");
  }
  
  public static void main(String args[]){
	  FileTreeOption1 option = FileTreeOption1.DEPTH;
	  option.setIntValue(3);
	  System.out.println(option.getIntValue());  // 3
	  option = FileTreeOption1.DATE;
	  System.out.println(option.getIntValue());  // UnsupportedOperationException
  }
}
英文:

Thanks to the help of Nikolai, I have just found out that not only can you override methods in an enum constant, but you can add variables to them too. So this one actually works:

public enum FileTreeOption1 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH{
	 private int intValue; // attribute only available to DEPTH
    @Override
    public void setIntValue(int value){
      this.intValue = value;
    }
    @Override
    public int getIntValue(){
      return intValue;
    }
  };

  public void setIntValue(int value) {
      throw new UnsupportedOperationException("setIntValue not available for " + this.name());
  }
  public int getIntValue() {
      throw new UnsupportedOperationException("getIntValue not available for " + this.name());
  }
  
  public static void main(String args[]){
	  FileTreeOption1 option = FileTreeOption1.DEPTH;
	  option.setIntValue(3);
	  System.out.println(option.getIntValue());  // 3
	  option = FileTreeOption1.DATE;
	  System.out.println(option.getIntValue());  // UnsupportedOperationException
  }
}

</details>



# 答案2
**得分**: 1

你必须将 `intValue` 的可见性从 `private` 更改为 `protected`,以使代码能够编译。

<details>
<summary>英文:</summary>

You must change `intValue` visibility from `private` to `protected` to make the code compile

</details>



# 答案3
**得分**: 1

以下是翻译好的部分:

```java
public enum FileTreeOption1 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH{
    @Override
    public void setIntValue(int value){
      throw new UnsupportedOperationException();
    }
    @Override
    public int getIntValue(){
      return 1;
    }
  } ;

  private int intValue;

  public void setIntValue(int value) {
      this.intValue = value;
  }
  public int getIntValue() {
      return intValue;
  }
}
英文:

For some more context: the code below does compile, so you are in fact allowed to have state in an enum, and to implement one or more of the methods in a different way for one or more of the constants:

public enum FileTreeOption1 {
  HUMAN_READABLE, DATE, DIRS_ONLY, FILES_ONLY, 
  DEPTH{
    @Override
    public void setIntValue(int value){
      throw new UnsupportedOperationException();
    }
    @Override
    public int getIntValue(){
      return 1;
    }
  } ;

  private int intValue;


  public void setIntValue(int value) {
      this.intValue = value;
  }
  public int getIntValue() {
      return intValue;
  }
}

答案4

得分: 0

错误:<strike>
对于你的问题的简短回答是 - 不可以。这段代码不应该能够编译。

解释:

首先,枚举是无法被扩展的。当你尝试定义你的 DEPTH{ 时,实际上你正在创建一个匿名类来扩展你的枚举。在Java中,这是不可能的。这也是第一个错误的原因:private int intValue; 是私有字段 - 因此在子类中是不可见的。
</strike>

这段代码无法编译,因为 private int intValue; 使用了 private 修饰符,而当你创建你的 DEPTH{ 时,你无法在那里访问 private 字段。只能使用 public 或者 protected

静态上下文 - Java 的 enums 默认是 public static final 的(你可以在Eclipse的大纲窗格中看到这一点)。

英文:

WRONG: <strike>
Short answer to your question - no. The code should not compile.

Explanation:

First of all - enum cannot be extended. When you're trying to define your DEPTH{ - you're actually creating anonymous class that extends your enum. In Java it's not possilbe. The same also is a reason to first error: private int intValue; is private field - thus it is not visible inside subclasses.
</strike>

The code does not compile because private int intValue; has private modifier and when you create your DEPTH{ - you cannot access private field there. Only public or protected.

The static context - Java enums are public static final by default (You can see this in the outline pane in Eclipse).

huangapple
  • 本文由 发表于 2020年8月31日 15:54:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/63666879.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定