I was quite surprised of the output of the following code:
Country class
public class Country {
private static Map<String, Country> countries = new HashMap<String, Country>();
private final String name;
@SuppressWarnings("LeakingThisInConstructor")
protected Country(String name) {
this.name = name;
register(this);
}
/** Get country by name */
public static Country getCountry(String name) {
return countries.get(name);
}
/** Register country into map */
public static void register(Country country) {
countries.put(country.name, country);
}
@Override
public String toString() {
return name;
}
/** Countries in Europe */
public static class EuropeCountry extends Country {
public static final EuropeCountry SPAIN = new EuropeCountry("Spain");
public static final EuropeCountry FRANCE = new EuropeCountry("France");
protected EuropeCountry(String name) {
super(name);
}
}
}
Main method
System.out.println(Country.getCountry("Spain"));
Output
null
Is there any clean way of forcing the class that extend Country to be loaded so the countries map contains all the Country instances?
The only way to initialize static final variables other than the declaration statement is Static block. A static block is a block of code with a static keyword. In general, these are used to initialize the static members. JVM executes static blocks before the main method at the time of class loading.
Static variables are initialized only once , at the start of the execution. These variables will be initialized first, before the initialization of any instance variables. A single copy to be shared by all instances of the class. A static variable can be accessed directly by the class name and doesn't need any object.
If you declare a static variable in a class, if you haven't initialized it, just like with instance variables compiler initializes these with default values in the default constructor. Yes, you can also initialize these values using the constructor.
Yes, use static initializer block:
public class Country {
private static Map<String, Country> countries = new HashMap<String, Country>();
static {
countries.put("Spain", new EuroCountry("Spain"));
}
...
Your class EuropeCountry
was not loaded at the time you called Country.getCountry("Spain")
. The correct solution would be
private static Map<String, Country> countries = new HashMap<String, Country>();
static {
// Do something to load the subclass
try {
Class.forName(EuropeCountry.class.getName());
} catch (Exception ignore) {}
}
This is just an example... There are other ways to achieve the same (see also Peter's answer)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With