代码之家  ›  专栏  ›  技术社区  ›  Mark

如何通过重写方法来使用javaenum中的字段?[副本]

  •  30
  • Mark  · 技术社区  · 10 年前

    任务是用java实现漂亮的策略设计模式 enum :

    public enum MyEnum {
    
        FIRST {
            @Override
            public String doIt() {
                return "1: " + someField; //error
            }
        },
        SECOND {
            @Override
            public String doIt() {
                return "2: " + someField; //error
            }
        };
    
        private String someField;
    
        public abstract String doIt();
    
    } 
    

    但当提到 someField 我明白了

    无法对非静态字段someField进行静态引用。

    有什么问题,有可能做得更好吗?

    8 回复  |  直到 10 年前
        1
  •  29
  •   Rafael Winterhalter    10 年前

    专业的 enum 只是一个具有内部类语义的子类。如果查看编译后的字节代码,您会注意到编译器只插入用于读取私有字段的访问器方法,但任何专用枚举都编译为自己的类。你可以考虑 枚举 实施为:

    public abstract class MyEnum {
    
      private static class First extends MyEnum {
    
        @Override
        public String doIt() {
            return "1: " + someField; //error
        }
      }
    
      private static class Second extends MyEnum {
    
        @Override
        public String doIt() {
            return "2: " + someField; //error
        }
      }
    
      public static final MyEnum FIRST = new First();
      public static final MyEnum SECOND = new Second();
    
      private String someField;
    
      public abstract String doIt();
    } 
    

    如您所见,同样的编译器错误也会发生。实际上,您的问题与 枚举 而是它们的内部类语义。

    然而,您发现编译器猜测代码的意图并试图警告您的意图是非法的,这是一种临界情况。一般来说 someField 任何专业人员都可以看到该字段 枚举 。但是,有两种方法可以访问 private 字段,只有一个是合法的:

    1. 私有的 成员不会被继承。因此,您无法访问 私有的 来自的字段 this 当它在一个超级类中定义时。

    2. 对于内部类,即使外部类的成员是 私有的 。这是由编译器通过将访问器方法插入到公开 私有的 字段。非- static 只有当内部类不是- 静止的 对于 枚举 s、 然而,内部类总是 静止的 .

    后一种情况是编译器抱怨的:

    无法对非静态字段进行静态引用 someField(someField)

    您正在尝试访问非- 静止的 来自的字段 静止的 内部类。这是不可能的,尽管由于内部类语义,字段在技术上是可见的。您可以通过以下方式明确指示编译器从超级类中读取该值:

    public String doIt() {
      MyEnum thiz = this;
      return thiz.someField;
    }
    

    现在编译器知道您正在尝试访问可见(外部)类型的成员,而不是错误地访问 someField(someField) (非静态)外部类实例(不存在)的字段。(同样,你可以写 super.someField 这表达了相同的想法,即您希望沿着继承链向下,而不访问外部实例的字段。)然而,更简单的解决方案是 protected 这样,编译器对继承可见性感到满意,并编译原始设置。

        2
  •  5
  •   Alex - GlassEditor.com    10 年前

    如果你 someField 受保护而非私人或使用 super.someField 相反,您将能够访问它。

        3
  •  2
  •   Tim B    10 年前

    someField 是私有的,请删除私有修饰符或将其移动到抽象类中。

        4
  •  2
  •   icza    10 年前

    私有字段不能从子类访问,这正是您在实现 MyEnum.doIt() 基于每个实例的抽象方法。将其更改为 protected ,它会起作用的。

        5
  •  1
  •   Szymon Krawczyk    10 年前

    当枚举是静态变量时,someField是私有变量。不能以这种方式将非静态变量分配给静态变量。

        6
  •  1
  •   Community c0D3l0g1c    7 年前

    显然问题是,当你说:

    public enum MyEnum {
        ...
        public abstract String doIt();
    } 
    

    它隐式需要枚举为 abstract “类”,因为您必须为它提供一个实现

    FIRST {
        @Override
        public String doIt() {
            return "1: " + this.someField; //error
        }
    }
    

    它给出一个错误,因为您正在尝试访问“基类” MyEnum 的私有字段,由于它是私有的,因此在隐式创建的匿名子类中不可见。像这样的 protected 在子类中是可见的,因此它解决了问题。

    关于堆栈溢出,有一些问题谈到了这个问题,例如 Singletons, Enums and anonymous inner classes Why can I anonymously subclass an enum but not a final class? .

    编辑:显然,这句话中的所有内容都不正确,因为 this.someField 不工作,因为该字段在子类中不可见 可视访问方式 super.someField 。这是我以前从未见过的现象,现在将尝试调查。

        7
  •  1
  •   Zaq    10 年前

    您可以做的是:

    public enum MyEnum {
        FIRST,SECOND;
    
        private String someField;
    
        public String doIt(){
            switch(this){
                case FIRST:  return "1: " + someField; break;
                case SECOND: return "2: " + someField; break;
            }
        }
    
    }
    

    这样,你仍然可以继承 Enum 你可以使用 MyEnum.values() 以及从中获得的其他福利 枚举 .

        8
  •  0
  •   Roger C S Wernersson    9 年前

    我不会使用枚举实现Strategy模式。所有代码都以相同的unti(文件)结尾。

    想法是分离代码。使用一个接口作为基类,然后将每个策略作为单独的子类来实现。干干净净。