When calling an enum constructor with a constant of the same class, the compiler requires its qualified name, as in the following sample:
enum Animal
{
CAT (MAMMAL), // this notation doesn't compile
LIZARD (Animal.REPTILE), // this one does
DOG (Animal.MAMMAL);
private static final int MAMMAL = 0;
private static final int REPTILE = 1;
private int family;
Animal(int family)
{
this.family = family;
}
}
Failing to comply results in an illegal forward reference: the compiler thinks of MAMMAL as an instance of this enum yet undeclared at this point (like DOG) instead of as a constant; this is ridiculous given that no constructor accepts a parameter of type Animal.
Why does the compiler take the pain to check whether MAMMAL is a constant or an instance of Animal only when using the syntax Animal.MAMMAL?
May be a bug in java compiler as describe here forward reference
Related
I am surprised that the first 2 lines of code in main below are allowed by the compiler. It seems to be trusting me when I say that getIt() will return a Dog even though the signature also allows a Cat to be returned.
public class GenericsAreWeird {
public static void main(String... args){
// This is fine
Dog d = getIt();
d.woof();
// This fails to compile
// getIt().woof();
// This compiles but gives a runtime ClassCastException
Cat c = getIt();
c.meow();
}
private static <T extends Animal> T getIt(){
return (T) new Dog();
}
public static class Animal {}
public static class Cat extends Animal{
public void meow(){}
}
public static class Dog extends Animal{
public void woof(){}
}
}
I generally trust Java generics to protect me from ClassCastExceptions by giving compiler errors, rather than telling me at runtime that something won't work, so allowing this seems broken.
It also seems counter-intuitive that:
Dog d = getIt();
d.woof();
is not exactly equivalent to:
getIt().woof();
the compiler needs the intermediate variable as a 'hint'.
Have generics always worked this way? Can someone point me to (or provide) a decent explanation of any other scenarios where the compiler fails to pick up a class-related error like this?
return (T) new Dog(); for the most part tells the compiler to
"shut up with your type checking, I know better than you that Dog will always be assignable to T"
to which the compiler replies
"okay, go ahead then, then you better take care of not ever calling this method expecting it to return a anything else / aCat"
Then you do call it expecting it to return a Cat and get a deserved ClassCastException. You get a runtime exception because you tell the compiler that you know it better (which you do not).
After type erasure your getIt() method will look very much like this:
private static Animal getIt(){
return new Dog();
}
But before the type erasure happens, the compiler sees, that you use a generic return type T extends Animal for the method! And thus, once it sees the line
Dog d = getIt();
it says: Ok, so T will be Dog in this particular case, so I need to insert a cast to Dog after type erasure like this:
Dog d = (Dog) getIt();
In the same way, for the call Cat c = getIt() it will resolve the type T to be Cat and add an according cast to Cat:
Cat c = (Cat) getIt();
But inside of your method the compiler warns you: Hey, are you sure, that the Dog you create will always be of type T? Remember, this is not guaranteed generally! So if you ignore this warning, you should know, what you are doing, taht's the point.
And finally, of course the call
getIt().woof()
will not compile, because in this case the compiler has no idea how to resolve the type T, so it will be just Animal, the upper limitation of T. It is not able to guess that it is a Dog only based on the call to the woof() method.
The following line:
getIt().woof();
fails because woof() is defined on Dog not Animal and getIt() returns a sub-class of Animal. Hence the compiler is unaware of woof().
You could try:
abstract class Animal { abstract void speak(); }
class Dog extends Animal { void speak() { // Dog specific implementation } }
class Cat extends Animal { void speak() { // Cat specific implementation } }
Then you could use:
getIt().speak();
You get a ClassCastException because the only information about the type returned by getIt() is that it is a sub-class of Animal. Assigning it to a variable of type Cat or Dog is unsafe. This would be true of any similar use of generics.
A common workaround is to define getIt() something like this:
<T extends Animal> T getIt(Class<T> clazz) {
// return an instance of the appropriate type based on clazz
}
You can use it thus:
Dog dog = getIt(Dog.class);
Type parameter E in Enum is defined as <E extends Enum<E>>. So why in Enum implementation source code, we still need to check getClass() and getDeclaringClass() in compareTo method? I don't think compiler can pass when I set a different enum type object in compareTo.
It covers the case of comparing raw types and values obtained via unsafe / unchecked casts and conversions (such as Comparable, or Object a):
static enum Fruit { Apple, Orange, Banana };
static enum Animal { Cat, Dog, Horse };
public static final void main (String[] args) throws Exception {
Enum f = Fruit.Apple;
Enum a = Animal.Cat;
f.compareTo(a);
}
There, compareTo would fail with a ClassCastException at the explicit getDeclaringClass comparison, as it would pass the first explicit cast (Enum other = (Enum)o) with no issue.
As for comparing getClass, it's tagged as an "optimization" in that source. The reason this is an optimization is that if the value classes are the same, then they're definitely from the same enum, and so there's no need to call the slightly more expensive getDeclaringClass. Since the vast majority of enums are likely simple enums (no value class bodies), it's optimized for that case.
It can, if you use Enum as a raw type. For instance in this program:
public static void main(String[] args) {
Enum e = A.x; // All occurrences of E are erased to Enum
e.compareTo(B.i); // B.i extends Enum, so this is fine
}
enum A {x,y,z};
enum B {i,j,k};
The values of an enumerated type are static variables of that type.
Far as i know, the variables are referenced, by the refererence variable but there is no new operator to instantiate an enum object. But is it like how you can initialize an array?
Is this true or false?
Yes, the literals of an enum type are public static final variables.
Simplified, it looks like this:
public final enum FooEnum {
A, B
}
public final class BarEnum {
public static final BarEnum A = new BarEnum();
public static final BarEnum B = new BarEnum();
}
In reality, there is a bit more stuff there, such as the list of all enumeration literals, a String identifier (the enum value knows its name), an ordinal number and a private Constructor to prevent instantiation (all omitted for clarity of code since question was only about the static)
Afaik enums are converted to classes and yes the values are static fields in this class: http://theopentutorials.com/tutorials/java/enum/enum-converted-to-class/
This is a design similar to other JPA BaseEntity patterns you may have seen:
#MappedSuperclass()
public abstract class Entity<X extends Entity<X>>
implements
Comparable<X>,
Serializable
{
private static final long serialVersionUID = 1L;
private Long id;
private Date timeStamp;
...
// Simply compare fields in subclass until difference is discovered
private int compareSubclassFields(X that)
{
int result = 0;
for(Comparator<X> comparator : getComparators())
{
result = comparator.compare(this,that); <<=== compilation error
if(result != 0) { break; }
}
return result;
}
/**
* Entity subclasses provide a list of their own special
* comparators that can contribute to the comparison.
*/
protected abstract List<Comparator<X>> getComparators();
}
Here is an example of a class that extends Entity:
public class User extends Entity<User>
{
private static final long serialVersionUID = 1L;
private String firstName;
private String lastName;
private String email;
...
#Override
public List<Comparator<User>> getComparators()
{
List<Comparator<User>> result =
new ArrayList<Comparator<User>>();
result.add(getLastNameComparator()); // Sort first on last name
result.add(getFirstNameComparator());// Next, by first name
result.add(getEmailComparator()); // Finally, by email (unique)
return result;
}
}
When I compile, I get the following error:
error: method compare in interface Comparator<T> cannot be
applied to given types;
result = comparator.compare(this,that);
^
required: X,X
found: Entity<X>,X
reason: actual argument Entity<X> cannot be converted to
X by method invocation conversion
where X,T are type-variables:
X extends Entity<X> declared in class Entity
T extends Object declared in interface Comparator
Reading Java Enum Definition, in particular the part where it says,
public class StatusCode extends Enum<StatusCode>
Now if you check the constraints, we've got Enum - so E=StatusCode. Let's check: does E extend Enum? Yes! We're okay.
I assume that in my example, where X extends Entity<X>, 'this' would be an instance of User and not Entity<User>. Moreover, because Entity is an abstract class it must be extended and, therefore, compareNonIdFields can only be invoked by an instance of X -- on itself. Of course, when I cast I get the unchecked warning:
warning: [unchecked] unchecked cast
result = comparator.compare(((X)this),that);
^
required: X
found: Entity<X>
where X is a type-variable:
X extends Entity<X> declared in class Entity
1 warning
Thoughts on why this recursive generic usage causes a compilation error and solutions to make the unchecked cast warning go away would be greatly appreciated.
You are writing this keyword inside the Entity<X> class. So,
this = Entity<X>
On the other hand, you provided Comparator for X, not for Entity<X>.
You may keep a field to store related X object inside Entity<X> object and write in this manner:
result = comparator.compare(this.getX(),that);
Imagine the following two classes.
class Foo extends Entity<Bar> {}
class Bar extends Entity<Foo> {}
Clearly the comparison can not only be invoked on instances of X: If you invoke it on an instance of Foo then X = Bar and vice versa.
Edit: Just to be clear, while you intend to always substitute the inheriting type itself for X, this is not enforced by the language and/or the compiler. That's the source of your issue.
Weird! It's perfectly happy if you
result = comparator.compare((X)this, that);
So, are there any circumstances under which "this" might not be an X? Some odd permutation of subclassing and leaving the parameter unbound? Or further subclassing a subclass with a bound parameter?
A ha! It can happen if you subclass the class when X is already bound!
… no, that's not right. I gotta admit, I'm flummoxed.
I am sure this type of question is asked every day, but I am struggling with understanding why the mammal object is instantiated as a cat, and reports its name to be Morris. However, unlike most cats, it does not have 9 lives. If the object is not a cat, why does it report its name to be Morris?
class Mammal {
int temp;
String name = "George";
public Mammal() {
temp = 98;
}
public String getName() {
return name;
}
}
class Cat extends Mammal {
int lives;
String name = "Morris";
public Cat() {
lives = 9;
}
public String getName() {
return name;
}
}
public class Inheritance {
public static void main(String[] args) {
Mammal mm = new Cat();
System.out.println("Mam Temp:" + mm.temp);
//System.out.println("Cat Lives:" + mm.lives); <-- error
System.out.println("Mam Name:" + mm.getName());
}
}
There are two related but concretely separate concepts here.
The runtime type of an object.
The static (and runtime) type of a variable.
The runtime type of the object will determine how it behaves at runtime. It's determined by the constructor you called when you used the new keyword.
The static type of the variable dictates how your code can interact with or reference that variable (and the object it refers to). It's determined by the type you specify when you declare that variable (or field, parameter, etc).
In this case, the static type of the variable is Mammal while the runtime type of the object it points to is Cat. Thus, while it behaves like a Cat at runtime and shows its name as being 'Morris', at compile time the compiler only knows that it is a Mammal, and cannot refer to the lives variable (which is defined in Cat, not Mammal).
Because the variable is of type Mammal which was set in the line Mammal mm = new Cat();. The Mammal class does not contain a variable lives and therefore it throws the error. If you do ((Cat)mm).lives however, you should get the correct result because you are casting the mm variable to the type Cat (which it was initialized as).
EDIT
Also, you overrode the getName() Method in the Cat class which "hides" the one in the Mammal class. You created mm as a Type Mammal but in reality it's a Cat so a call to the getName() method on mm will call the child method getName() which returns "Morris" instead of "George".
You created an instance of Cat but you are treating it as a Mammal. Mammal.lives doesn't exist, which is why you get an error when you try to print mm.lives.
Incidentally, this is a good example of why inheritance can be dangerous and should be used sparingly. Cat and Mammal are tightly coupled by inheritance, so you have to understand both classes and how they interact in order to understand code that deals with Cat.
the object created is that of Cat so the getName method when invoked is on the Cat object and hence its name is reported as "morris". The local variable however is defined as a Mammal so lives can not be accessed unless you explicitly cast it to a cat.