เหตุใด Enum จึงใช้อินเทอร์เฟซ


คำตอบ:


130

Enums ไม่จำเป็นต้องเป็นตัวแทนของเซตแบบพาสซีฟ (เช่นสี) พวกเขาสามารถแสดงวัตถุที่ซับซ้อนมากขึ้นกับการทำงานและเพื่อให้คุณจากนั้นก็มีแนวโน้มที่จะต้องการที่จะเพิ่มฟังก์ชันการทำงานต่อไปเหล่านี้ - เช่นคุณอาจจะมีการเชื่อมต่อเช่นPrintable, Reportableฯลฯ และส่วนประกอบที่สนับสนุนเหล่านี้


259

นี่คือตัวอย่างหนึ่ง (พบ / คล้ายกันที่ดีกว่าใน Effective Java 2nd Edition):

public interface Operator {
    int apply (int a, int b);
}

public enum SimpleOperators implements Operator {
    PLUS { 
        int apply(int a, int b) { return a + b; }
    },
    MINUS { 
        int apply(int a, int b) { return a - b; }
    };
}

public enum ComplexOperators implements Operator {
    // can't think of an example right now :-/
}

ตอนนี้เพื่อรับรายชื่อทั้ง Simple + Complex Operators:

List<Operator> operators = new ArrayList<Operator>();

operators.addAll(Arrays.asList(SimpleOperators.values()));
operators.addAll(Arrays.asList(ComplexOperators.values()));

ดังนั้นที่นี่คุณจะใช้อินเทอร์เฟซเพื่อจำลอง enums ที่ขยายได้ (ซึ่งไม่สามารถทำได้โดยไม่ต้องใช้อินเตอร์เฟส)


3
ผู้ประกอบการที่ซับซ้อนอาจเป็น "ธาร" และไม่ชอบ
Pimgd

2
ครั้งแรกที่เห็น Enum syntax (ด้วยเครื่องหมายปีกกาหลังสมาชิก) และฉันได้เขียนโปรแกรมกับ Java เป็นเวลา 6 ปี TIL
jrahhali

23

Comparableตัวอย่างที่ได้รับจากคนหลายคนที่นี่เป็นสิ่งที่ผิดตั้งแต่Enumอยู่แล้วว่าการดำเนินการ คุณไม่สามารถแทนที่มันได้

ตัวอย่างที่ดีกว่าคือมีอินเทอร์เฟซที่กำหนดสมมติว่าเป็นชนิดข้อมูล คุณสามารถมี enum เพื่อใช้ชนิดง่าย ๆ และมีคลาสปกติเพื่อใช้ชนิดที่ซับซ้อน:

interface DataType {
  // methods here
}

enum SimpleDataType implements DataType {
  INTEGER, STRING;

  // implement methods
}

class IdentifierDataType implements DataType {
  // implement interface and maybe add more specific methods
}

2
ใช่แปลก !! Enum ใช้อินเทอร์เฟซที่เปรียบเทียบได้แล้วและไม่อนุญาตให้แทนที่ ฉันค้นหาซอร์สโค้ดจาวาสำหรับคลาสเอนัมและไม่พบการใช้งาน comparTo ทุกคนสามารถบอกสิ่งที่ได้รับการเปรียบเทียบในวิธีการเปรียบเทียบกับ ENUM?
Akh

2
@ AKh จะเปรียบเทียบลำดับซึ่งหมายความว่าการเรียงลำดับตามธรรมชาติคือลำดับที่ค่าคงที่ enum อยู่ในไฟล์ต้นฉบับ
จอน

Enum vars ถือเป็นที่สุดดังนั้นการเปรียบเทียบจะเกิดขึ้นกับ == ใช่ไหม
djangofan

@dangangofan ใช่
Jorn

18

มีบางกรณีที่ฉันมักจะใช้ ฉันมีIdUtilชั้นเรียนที่มีวิธีคงที่ในการทำงานกับวัตถุที่ใช้Identifiableอินเตอร์เฟซที่เรียบง่ายมาก:

public interface Identifiable<K> {
    K getId();
}


public abstract class IdUtil {

    public static <T extends Enum<T> & Identifiable<S>, S> T get(Class<T> type, S id) {
        for (T t : type.getEnumConstants()) {
            if (Util.equals(t.getId(), id)) {
                return t;
            }
        }
        return null;
    }

    public static <T extends Enum<T> & Identifiable<S>, S extends Comparable<? super S>> List<T> getLower(T en) {
        List<T> list = new ArrayList<>();
        for (T t : en.getDeclaringClass().getEnumConstants()) {
             if (t.getId().compareTo(en.getId()) < 0) {
                 list.add(t);
            }
        }
        return list;
    }
}

ถ้าฉันสร้างIdentifiable enum:

    public enum MyEnum implements Identifiable<Integer> {

        FIRST(1), SECOND(2);

        private int id;

        private MyEnum(int id) {
            this.id = id;
        }

        public Integer getId() {
            return id;
        }

    }

จากนั้นฉันจะได้รับโดยidวิธีนี้:

MyEnum e = IdUtil.get(MyEnum.class, 1);

สิ่งที่ฉันต้องการ!
Petronella

13

เนื่องจาก Enums สามารถใช้อินเตอร์เฟสได้จึงสามารถใช้สำหรับการบังคับใช้รูปแบบซิงเกิลที่เข้มงวด การพยายามทำให้คลาสมาตรฐานเป็นแบบซิงเกิลอนุญาตให้ ...

  • สำหรับความเป็นไปได้ของการใช้เทคนิคการสะท้อนเพื่อเปิดเผยวิธีการส่วนตัวในที่สาธารณะ
  • สำหรับการสืบทอดจากซิงเกิลของคุณและเอาชนะวิธีการของซิงเกิลด้วยสิ่งอื่น

Enums เป็น singletons ช่วยป้องกันปัญหาด้านความปลอดภัยเหล่านี้ นี่อาจเป็นหนึ่งในสาเหตุที่ทำให้ Enums ทำหน้าที่เป็นคลาสและใช้อินเทอร์เฟซ เพียงแค่เดา

ดู/programming/427902/java-enum-singletonและSingleton class ใน javaเพื่อการสนทนาเพิ่มเติม


1
for inheriting from your singleton and overriding your singleton's methods with something else. คุณสามารถใช้ a final classเพื่อป้องกันสิ่งนั้นได้
Dylanthepiguy

10

มันจำเป็นสำหรับการเพิ่ม - ถ้ามีคนใช้ API ที่คุณพัฒนาขึ้นจำนวนเงินที่คุณกำหนดจะคงที่ ไม่สามารถเพิ่มหรือแก้ไขได้ อย่างไรก็ตามถ้าคุณปล่อยให้มันใช้อินเตอร์เฟสคนที่ใช้ API สามารถพัฒนา enum ของตัวเองโดยใช้อินเตอร์เฟสเดียวกัน จากนั้นคุณสามารถลงทะเบียน enum นี้ด้วยตัวจัดการ enum ซึ่งจะทำการรวม enums เข้ากับอินเตอร์เฟสมาตรฐาน

แก้ไข: @Helper Method มีตัวอย่างที่สมบูรณ์แบบ ลองนึกถึงการมีห้องสมุดอื่น ๆ ที่กำหนดผู้ประกอบการรายใหม่แล้วบอกกับผู้จัดการของชั้นว่า มิฉะนั้นคุณจะสามารถกำหนดผู้ประกอบการในรหัสของคุณเท่านั้น - ไม่มีความสามารถในการขยาย


7

Enums เป็นเพียงคลาสปลอมตัวดังนั้นส่วนใหญ่สิ่งที่คุณสามารถทำได้กับคลาสที่คุณสามารถทำได้ด้วย Enum

ฉันไม่สามารถนึกถึงเหตุผลที่ enum ไม่สามารถใช้ส่วนติดต่อได้ในเวลาเดียวกันฉันไม่สามารถคิดเหตุผลที่ดีสำหรับพวกเขาได้เช่นกัน

ฉันจะบอกว่าเมื่อคุณเริ่มเพิ่มสิ่งต่าง ๆ เช่นอินเทอร์เฟซหรือวิธีการ enum คุณควรพิจารณาทำให้คลาสแทน แน่นอนฉันแน่ใจว่ามีกรณีที่ถูกต้องสำหรับการทำสิ่งที่ไม่ใช่แบบดั้งเดิม enum และเนื่องจากขีด จำกัด จะเป็นสิ่งประดิษฐ์ฉันจึงยินยอมให้ผู้คนทำในสิ่งที่พวกเขาต้องการ


4
"... ดังนั้นส่วนใหญ่คุณสามารถทำอะไรกับคลาสที่คุณสามารถทำกับ enum" แต่ฉันไม่สามารถพูดได้ว่า enum ของฉันสืบทอดมาจากคลาสนามธรรม ดังนั้นใช่เน้นที่ "ส่วนใหญ่"
Adam Parkin

5
นั่นเป็นเพราะ enums ขยายทั้งหมด java.lang.Enum และคลาส Java สามารถขยายจากชั้นหนึ่งเท่านั้น
TofuBeer

6

ตัวอย่างเช่นถ้าคุณมี Logger enum จากนั้นคุณควรมีวิธีการบันทึกเช่นดีบักข้อมูลคำเตือนและข้อผิดพลาดในอินเทอร์เฟซ มันทำให้โค้ดของคุณมีการรวมกันอย่างหลวม ๆ


6

การใช้งานทั่วไปส่วนใหญ่สำหรับสิ่งนี้คือการรวมค่าของสอง enums ไว้ในกลุ่มเดียวและปฏิบัติต่อพวกมันในทำนองเดียวกัน ยกตัวอย่างเช่นดูวิธีการเข้าร่วมผักและผล Vegatables


5

หนึ่งในกรณีที่ดีที่สุดสำหรับฉันที่จะใช้ Enum กับอินเทอร์เฟซคือตัวกรองเพรดิเคต เป็นวิธีที่ยอดเยี่ยมมากในการแก้ไขการขาดอาปาเช่คอลเลคชั่น (ถ้าไม่มีไลบรารีอื่น ๆ )

import java.util.ArrayList;
import java.util.Collection;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;


public class Test {
    public final static String DEFAULT_COMPONENT = "Default";

    enum FilterTest implements Predicate {
        Active(false) {
            @Override
            boolean eval(Test test) {
                return test.active;
            }
        },
        DefaultComponent(true) {
            @Override
            boolean eval(Test test) {
                return DEFAULT_COMPONENT.equals(test.component);
            }
        }

        ;

        private boolean defaultValue;

        private FilterTest(boolean defautValue) {
            this.defaultValue = defautValue;
        }

        abstract boolean eval(Test test);

        public boolean evaluate(Object o) {
            if (o instanceof Test) {
                return eval((Test)o);
            }
            return defaultValue;
        }

    }

    private boolean active = true;
    private String component = DEFAULT_COMPONENT;

    public static void main(String[] args) {
        Collection<Test> tests = new ArrayList<Test>();
        tests.add(new Test());

        CollectionUtils.filter(tests, FilterTest.Active);
    }
}

5

โพสต์ข้างต้นที่กล่าวถึงกลยุทธ์นั้นไม่ได้เครียดเพียงพอที่การใช้รูปแบบกลยุทธ์ที่มีน้ำหนักเบาโดยใช้ enums จะทำให้คุณได้รับ:

public enum Strategy {

  A {
    @Override
    void execute() {
      System.out.print("Executing strategy A");
    }
  },

  B {
    @Override
    void execute() {
      System.out.print("Executing strategy B");
    }
  };

  abstract void execute();
}

คุณสามารถมีกลยุทธ์ทั้งหมดของคุณในที่เดียวโดยไม่จำเป็นต้องมีหน่วยการรวบรวมแยกต่างหากสำหรับแต่ละหน่วย คุณจะได้รับการจัดส่งแบบไดนามิกที่ดีเพียงแค่:

Strategy.valueOf("A").execute();

ทำให้ java อ่านเกือบจะเป็นภาษาที่พิมพ์ดีอย่างหลวม ๆ !


3

Enums เป็นเหมือนเรียน Java พวกเขาสามารถมีก่อสร้าง, วิธีอื่น ๆ new EnumName()มีเพียงสิ่งเดียวที่คุณไม่สามารถทำอะไรกับพวกเขาก็คือ อินสแตนซ์ถูกกำหนดไว้ล่วงหน้าในการประกาศ enum ของคุณ


เกี่ยวกับenum Foo extends SomeOtherClassอะไร ดังนั้นจึงไม่ใช่สิ่งเดียวกันกับชั้นเรียนปกติที่จริงแล้วค่อนข้างแตกต่างกัน
Adam Parkin

@ อดัม: ฉันจะบอกว่าวิธีการที่ Enums คล้ายกับคลาสนั้นมีมากมายมากกว่าวิธีที่พวกเขาแตกต่างจากคลาส แต่ไม่พวกเขาไม่เหมือนกัน
กอตต์

ถ้าคุณแปลค่า enum คุณจะเห็นว่าเป็นคลาสที่ขยายการแจงนับและมี "ค่าคงที่" อินสแตนซ์แล้วในเขตข้อมูลตัวสร้าง / การเริ่มต้น
Cosmin Cosmin

3

ตำแหน่งอื่น:

public enum ConditionsToBeSatisfied implements Predicate<Number> {
    IS_NOT_NULL(Objects::nonNull, "Item is null"),
    IS_NOT_AN_INTEGER(item -> item instanceof Integer, "Item is not an integer"),
    IS_POSITIVE(item -> item instanceof Integer && (Integer) item > 0, "Item is negative");

    private final Predicate<Number> predicate;
    private final String notSatisfiedLogMessage;

    ConditionsToBeSatisfied(final Predicate<Number> predicate, final String notSatisfiedLogMessage) {
        this.predicate = predicate;
        this.notSatisfiedLogMessage = notSatisfiedLogMessage;
    }

    @Override
    public boolean test(final Number item) {
        final boolean isNotValid = predicate.negate().test(item);

        if (isNotValid) {
            log.warn("Invalid {}. Cause: {}", item, notSatisfiedLogMessage);
        }

        return predicate.test(item);
    }
}

และการใช้:

Predicate<Number> p = IS_NOT_NULL.and(IS_NOT_AN_INTEGER).and(IS_POSITIVE);

3

เมื่อสร้างค่าคงที่ในไฟล์ jar มักจะเป็นประโยชน์ในการให้ผู้ใช้ขยายค่า enum เราใช้ enums สำหรับคีย์ PropertyFile และค้างอยู่เพราะไม่มีใครสามารถเพิ่มคีย์ใหม่ได้! ด้านล่างจะทำงานได้ดีขึ้นมาก

ได้รับ:

public interface Color {
  String fetchName();
}

และ:

public class MarkTest {

  public static void main(String[] args) {
    MarkTest.showColor(Colors.BLUE);
    MarkTest.showColor(MyColors.BROWN);
  }

  private static void showColor(Color c) {
    System.out.println(c.fetchName());
  }
}

หนึ่งอาจมีหนึ่ง enum ในขวด:

public enum Colors implements Color {
  BLUE, RED, GREEN;
  @Override
  public String fetchName() {
    return this.name();
  }
}

และผู้ใช้สามารถขยายเพื่อเพิ่มสีของเขาเอง:

public enum MyColors implements Color {
  BROWN, GREEN, YELLOW;
  @Override
  public String fetchName() {
    return this.name();
  }
}

2

นี่คือเหตุผลของฉันทำไม ...

ฉันได้เติม JavaFX ComboBox ด้วยค่าของ Enum ฉันมีอินเทอร์เฟซที่สามารถระบุตัวตนได้ (ระบุวิธีการหนึ่ง: ระบุ) ที่ช่วยให้ฉันระบุว่าวัตถุใดที่ระบุตัวเองในแอปพลิเคชันของฉันเพื่อวัตถุประสงค์ในการค้นหา อินเทอร์เฟซนี้ช่วยให้ฉันสามารถสแกนรายการของวัตถุชนิดใดก็ได้ (เขตข้อมูลใดก็ตามที่วัตถุอาจใช้เพื่อระบุตัวตน) เพื่อจับคู่ข้อมูลประจำตัว

ฉันต้องการค้นหาการจับคู่สำหรับค่าตัวตนในรายการ ComboBox ของฉัน เพื่อที่จะใช้ความสามารถนี้ใน ComboBox ของฉันที่มีค่า Enum ฉันจะต้องสามารถใช้งานอินเทอร์เฟซที่สามารถระบุตัวตนได้ใน Enum ของฉัน (ซึ่งในขณะที่มันเกิดขึ้น


1

ฉันใช้ enum ด้านในในอินเทอร์เฟซที่อธิบายกลยุทธ์เพื่อควบคุมอินสแตนซ์ (แต่ละกลยุทธ์คือซิงเกิลตัน) จากที่นั่น

public interface VectorizeStrategy {

    /**
     * Keep instance control from here.
     * 
     * Concrete classes constructors should be package private.
     */
    enum ConcreteStrategy implements VectorizeStrategy {
        DEFAULT (new VectorizeImpl());

        private final VectorizeStrategy INSTANCE;

        ConcreteStrategy(VectorizeStrategy concreteStrategy) {
            INSTANCE = concreteStrategy;
        }

        @Override
        public VectorImageGridIntersections processImage(MarvinImage img) {
            return INSTANCE.processImage(img);
        }
    }

    /**
     * Should perform edge Detection in order to have lines, that can be vectorized.
     * 
     * @param img An Image suitable for edge detection.
     * 
     * @return the VectorImageGridIntersections representing img's vectors 
     * intersections with the grids.
     */
    VectorImageGridIntersections processImage(MarvinImage img);
}

ความจริงที่ว่า enum ใช้กลยุทธ์นั้นสะดวกเพื่อให้คลาส enum ทำหน้าที่เป็นพร็อกซีสำหรับอินสแตนซ์ที่ล้อมรอบ ซึ่งใช้อินเทอร์เฟซ

มันเป็นส่วนหนึ่งของ strategyEnumProxy: P รหัส clent มีลักษณะดังนี้:

VectorizeStrategy.ConcreteStrategy.DEFAULT.processImage(img);

ถ้ามันไม่ได้ใช้อินเทอร์เฟซมันเคยเป็น:

VectorizeStrategy.ConcreteStrategy.DEFAULT.getInstance().processImage(img);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.