Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generics and reflection: class loading

I am given some classes that are unknown to me. Some of them are shown as an example:

class Paper {}

class Bakery {}

class Cake extends Bakery {}

class ReflexiveBaker {

  /**
   * Create bakery of the provided class.
   * 
   * @param order class of bakery to create
   * @return bakery object
   */
  public Object bake(Class order) {
    // Add implementation here
  }

}

The task is to redesign method signature types if needed and to add implementation. The bake method should conform to the following:

  • Create objects of class Bakery or any subclass of it according to class argument
  • Flag compile-time error if order argument is not Bakery or any subclass of it (e.g. if it is Paper or Object)

Whatever I've tried I'm getting an error: Main.java:: error: incompatible types: Object cannot be converted to Cake Cake cake = baker.bake(Cake.class);

The best I've come up with is this:

public Object bake(Class<? extends Bakery> order) throws Exception {
        return order.getDeclaredConstructor().newInstance();
}

I know that it's wrong but I'm completely stuck here. Could someone please explain what's going on?

like image 641
bilarsen Avatar asked Jan 16 '20 08:01

bilarsen


People also ask

Are Java's dynamic class loading features part of Java reflection?

You can argue whether Java's dynamic class loading features are really part of Java Reflection, or a part of the core Java platform. Anyways, the article has been put in the Java Reflection trail in lack of a better place to put it. All classes in a Java application are loaded using some subclass of java.lang.ClassLoader .

How to reload a dynamic class in Java?

Dynamic class reloading is a bit more challenging. Java's builtin Class loaders always checks if a class is already loaded before loading it. Reloading the class is therefore not possible using Java's builtin class loaders. To reload a class you will have to implement your own ClassLoader subclass.

How to use generics in Java?

Using Java Generics typically falls into one of two different situations: Declaring a class/interface as being parameterizable. Using a parameterizable class. When you write a class or interface you can specify that it should be paramerizable. This is the case with the java.util.List interface.

What happens when a class is loaded in Java?

When a class is loaded, all classes it references are loaded too. This class loading pattern happens recursively, until all classes needed are loaded. This may not be all classes in the application. Unreferenced classes are not loaded until the time they are referenced.


1 Answers

Your methods return type is java.lang.Object and the compiler can't vouch that the return value is of type Cake unless you convince the compiler by adding an explicit unchecked cast like this.

Cake cake = (Cake) ReflexiveBaker.bake(Cake.class);

This unchecked cast is error prone and awkward. Say you have another class called Bread that is a subtype of Bakery and you pass that class instance expecting Cake as the return type. The above statement still compiles, but throws a ClassCastException at runtime.

A much better approach is to generify the method using a bounded type parameter such a way that it accepts only sub types of Bakery and returns the same type as the type parameter of the class object provided. Here's one such attempt.

static class ReflexiveBaker {
    public static <T extends Bakery> T bake(Class<T> order) {
        try {
            return order.getDeclaredConstructor().newInstance();
        } catch (NoSuchMethodException | InstantiationException | IllegalAccessException
                    | InvocationTargetException e) {
            throw new AssertionError("Class could not be instantiated.", e);
        }
    }
}
like image 90
Ravindra Ranwala Avatar answered Oct 03 '22 15:10

Ravindra Ranwala