Enum SUBtiliteit

 
30 maart 2009

Laatst klaagde mijn compiler tegen mij. Nu komt dit wel vaker voor, maar nu was ik het echt niet met haar eens. Sterker nog ik vond dat zij ongelijk had.
Onze discussie deed mij een subtiel punt over enum’s inzien. Voordat ik het je vertel moet je eerst aantwoord geven op de volgende vraag.

Vraag
Bekijk onderstaande code. Wat is het resultaat van het compileren ervan?

public enum Subtle {	
 
	A {
 
		@Override
		private String example() {
 
			return "override case";
		}
	}, B;
 
	private String example() {
 
		return "base case";
	}
 
	public String comment() {
 
		return this.example();
	}
}

De vraag is een strik vraag. De bovenstaande code compileert niet. De melding die de compiler geeft is:

method does not override or implement a method from a supertype

Dit was iets wat ik totaal niet verwacht had of zelfs begreep. Ik zag niet in wat er met de code mis was. Na enig puzzelen kwam het inzicht. Mijn ideeën over enum’s bleken onjuist!

Antwoord
Enum’s zijn sinds versie 1.5 opgenomen in het Java platform. Zij zijn zelf volwaardige klasse. Er kunnen dus constructors, methoden en velden gedefinieerd worden. Voor een uitgebreide introductie en motivatie van enum’s, verwijs ik naar de pagina van Sun.

Ik dacht tot voor kort dat enum constante instanties zijn van de klasse. Ik ben er nu achter gekomen dat dit maar ten dele waar is. De waarheid is subtieler.
Enum constante zijn alleen instanties van de klasse wanneer zij geen constante specifieke klasse body hebben. In een dergelijke body is het mogelijk methoden op te nemen die specifiek zijn voor de constante.
Effectief is een klasse body een voorbeeld van een anonieme klasse instantiatie. In een anonieme klasse instantiatie wordt in “één regel” een implementatie gegeven voor een interface of een klasse, en een object ervan geinstantieerd. Ik geef hieronder een voorbeeld.

public interface Greetor {
 
	public void greet();
}

Bovenstaande interface wordt hieronder in geïmplementeerd en een instantie geinstantieerd. De instantie blijft zelf ook naamloos en haar greet methode wordt aangeroepen.

		...		
		new Greetor() {
 
			public void greet() {
 
				System.out.println("Hello Anonymous World");
			}
		}.greet();
		...

Hieronder staan de klasse die de compiler genereerde.

  • Greetor
  • Example
  • Example$1

Een onverwachte klasse is misschien Example$1.class. De disassembler geeft de relatie aan tussen Example en Example$1.

final class nl.sogyo.blog.subtle.Example$1 extends java.lang.Object implements nl.sogyo.blog.subtle.Greetor

Wanneer we nu dezelfde disassembler gebruiken voor onze enum vinden soortgelijke informatie. (Zet om de code te compileren de annotatie @Override in commentaar.)

final class nl.sogyo.blog.subtle.Subtle$1 extends nl.sogyo.blog.subtle.Subtle

De compiler had gelijk. De private methode van Subtle was voor de sub-klasse Subtle$1 niet zichtbaar en kon dus ook niet overridden worden.

Conclusie
Enum constante met een specifiek klasse body is een voorbeeld van een anonieme klasse instantiatie.

Soms wordt je blij verrast door de compiler. Het zijn die momenten waarop je mis-concepties aan het licht komen. Door hier serieus op in te gaan wordt je begrip van de taal vergroot. Ik kan dus haast niet wachten op mijn volgende compileer fout.


Werken met ?
Kijk dan bij onze mogelijkheden voor zowel starters als ervaren engineers.


Categorieën: Development

Tags: , , , ,


Reacties (2)

  • Daan Wanrooy schreef:

    @Thomas

    Jij hebt natuurlijk helemaal gelijk. Private methode en velden zijn alleen zichtbaar binnen de klasse. Dit is mij bekend.

    Wat ik verbazend vond is dat de constante blijkbaar een instantie van een sub-klasse is! Zonder specifiek klasse body zou dat niet het geval zijn.

    Geplaatst op 30 maart 2009 om 15:34 Permalink

  • Thomas Zeeman schreef:

    Als ik naar het voorbeeld uit je vraag kijk, dan zie ik daar iets wat niet alleen voor enum’s geldt, maar voor alle classes in Java. Een override van een private methode kan simpelweg niet. Die private methode is in de child class namelijk niet zichtbaar.

    Probeer onderstaande classes maar eens te compileren, je zal zien dat dat ook een compile error oplevert:

    public class A {

    private String test() {
    return null;
    }
    }

    public class B extends A {

    @Override
    private String test() {
    return “B”;
    }
    }

    Kijk in Eclipse vooral ook naar wat de Quickfix als suggestie geeft.

    Geplaatst op 30 maart 2009 om 15:26 Permalink