Monday, November 14, 2022

Quiz yourself: The truth about Java enums

Java enums, Oracle Java Exam, Oracle Java Prep, Oracle Java Career, Oracle Java Skills, Oracle Java Skills, Oracle Java Prep


Which statements about enums are correct? Choose two.


A. You cannot declare a final method in the enum type.
B. You can declare an abstract method in the enum type.
C. enum instances are immutable.
D. You can override the equals method to implement your own comparison based on an enum constant’s state.
E. enum members with protected access modifiers cannot be accessed outside an enum’s own package.

Answer. The goal of the enum concept is to provide a specific number of instances of a given type. That number is fixed in the source code, and each instance has a well-defined name in the code.

Enums have a common parent class. It’s prohibited to describe another class as extends any enum. This rule ensures that no other class can be defined from which objects can be created that are assignment-compatible with the enum type. After all, if you were allowed to define new classes compatible with the enum type, you could then arbitrarily add new objects beyond the intended set of instances.

In addition, controls are imposed on any enum to ensure that deserialization and other behaviors do not create new, unintended instances.

Supporting these ideas is one of the effects of having the common parent class—but note that the enum must not declare that it extends that parent class. Instead, this happens automatically because of the use of the enum keyword in place of the class keyword.

These initial ideas have some consequences that are relevant to determining the answer to this question.

Option A suggests that you cannot declare a final method in an enum type. At first glance doing so might seem pointless, since the enum seems to be final. However, you can, in fact, define subtypes of the enum, provided you do so directly within the enum type itself, in the context of one of the instances.

It’s probably helpful to emphasize the distinction between the enum type and the instances of the enum.

Consider the sample code below; the enum type called Type has two instances: One is referred to using the reference GOOD, and the other is referred to using the reference BAD. However, the syntax used here, where GOOD is followed by curly braces, actually means that GOOD refers to an instance of an anonymous subtype of Type. Given that the example defines the doIt() method in the enum type Type as being final, this prevents overriding of that method in GOOD.

enum Type {
    GOOD {}, // cannot override doIt here
    BAD;
    final void doIt() {} // OK
}

Since subclassing the enum is possible, although only under these very specific syntactic constraints, it’s not unreasonable to declare a method as final, and it is, in fact, permitted. From this, it’s clear that option A is incorrect.

Now that you know that limited subtyping is possible with enum types, it’s reasonable to expect that you can usefully declare an abstract method inside an enum type, and that expectation is correct. In such a situation you must provide an implementation of the abstract method inside each enum constant’s body. You must not leave any abstract method unimplemented, because the enum type may not itself be declared as abstract.

enum Type { // cannot have abstract modifier
    GOOD {
        String describe() { return "Good"; }
    },
    BAD {
        String describe() { return "Not good"; }
    };
    // this is valid provided all the above objects implement
    // the method 
    abstract String describe(); 
}

From this you know that option B is correct.

An enum guarantees a specific number of instances of the type but does not guarantee immutability. Guaranteeing immutability is the responsibility of the developer. An enum constant is really a public static final reference to an instance of the enum type. While that reference cannot be modified, the object to which it refers might be mutable. This can happen if the enum is declared in such a way that it contains a mutable field, and the following code illustrates this:

enum Type {
    GOOD,
    BAD;
    // description is not final, so it can be modified
    public String description; 
    String describe() { return description; }
}

In this example, the public String field named description could be reassigned to refer to a different String object. Similarly, if the field were declared to be of the StringBuilder type, the contents of that object would be intrinsically mutable. From this you can see that option C is incorrect.

Option D is also incorrect: Each enum type implicitly inherits from the java.lang.Enum class, which already has some final methods, in particular, the following:

◉ compareTo()
◉ equals()
◉ hashCode()
◉ name()
◉ ordinal()

Because the equals() method is final in the base type Enum, you cannot override it, and you cannot implement your own comparison logic. The inherited implementation looks like this.

public final boolean equals(Object other) {
  return this==other;
}

Option E is correct, as protected members of Java classes (including enums) can be accessed only in a different package if the access is performed in a subtype that’s declared in that other package.

However, subclasses of an enum must be nested, that is, declared inside the top-level curly braces that contain the body of the enum declaration. Because of that, the subtypes of an enum cannot possibly be in a different package. Consequently, all protected members can be accessed only from the enum’s package and not from external packages. Their access is, therefore, indistinguishable from what it would be if they were declared with default accessibility, that is, without any explicit modifier.

Conclusion. The correct answers are options B and E.

Source: oracle.com

Related Posts

0 comments:

Post a Comment