Java generics are checked for type correctness at compile time, when a generic type is instantiated, the compiler removes generic type information by a type erasure technique. A generic class is translated to a raw type – generic type without any type arguments. Consequently, when a type is not known during both compile time and runtime, it is not possible to instantiate a generic type parameter using a constructor call. The following code:

  private I i= new I ();

will not compile.

In this post I’ll describe two possible solutions, both using java.lang.Reflection package.

Let’s assume we have a generic Envelope class, which is used to pair a content object – Content – with additional information related to it – ContentInfo.

Envelope.java

package com.application.generics;

import java.io.Serializable;

public class Envelope <I extends ContentInfo, C extends Serializable> implements Serializable {
  private static final long serialVersionUID = -6433250240547869806L;

  private I contentInfo;
  private C content;

  public Envelope () {
  }

  public Envelope (I contentInfo) {
    this.contentInfo = contentInfo;
  }

  public Envelope (I contentInfo, C content) {
    this.contentInfo = contentInfo;
    this.content = content;
  }

  public I getContentInfo() {
    return contentInfo;
  }

  public C getContent() {
    return content;
  }

  public void setContent(C content) {
    this.content = content;
  }

  public void setContentInfo(I contentInfo) {
    this.contentInfo = contentInfo;
  }

}

ContentInfo is an interface:

ContentInfo.java

package com.application.generics;

public interface ContentInfo {

  public boolean isContentValid();

}

And its three possible implementations:

ContentAlwaysValid.java

package com.application.client;

import java.io.Serializable;

import com.application.generics.ContentInfo;

public class ContentAlwaysValid implements ContentInfo, Serializable {
  private static final long serialVersionUID = 8019707740072299936L;

  public boolean isContentValid() {
    return true;
  }

}

ContentNeverValid .java

package com.application.client;

import java.io.Serializable;

import com.application.generics.ContentInfo;

public class ContentNeverValid implements ContentInfo, Serializable {
  private static final long serialVersionUID = -861952203103353570L;

  public boolean isContentValid() {
    return false;
  }

}

ContentSometimesValid.java

package com.application.client;

import java.io.Serializable;

import com.application.generics.ContentInfo;

public class ContentSometimesValid implements ContentInfo, Serializable {
  private static final long serialVersionUID = 2731488051426426010L;

  private boolean contentValid;

  public ContentSometimesValid (boolean contentValid) {
    this.contentValid = contentValid;
  }

    public boolean isContentValid() {
      return contentValid;
    }

}

Additionally, let’s say, there is requirement, that the contentInfo is initialized to its default value whenever an object of the Envelope class is instantiated. The common solution is a factory class, which provides a method which instantiates an object of a given class using Reflection API. In our case, a simple factory class – ContentWrapper – might look like this:

ContentWrapper.java

package com.application.generics;

import java.io.Serializable;

public class ContentWrapper <I extends ContentInfo, C extends Serializable> {

  @SuppressWarnings("unchecked")
  public Envelope <I, C> wrapContent (Class<?> cls, C content) {
    I contentInfo = null;
    try {
      contentInfo = (I) cls.newInstance();
    } catch (Exception exception) {
      throw new RuntimeException(exception);
    }
    return new Envelope <I, C> (contentInfo, content);
  }

}

ContentWrapper does the job. The following code:

ContentWrapperTest.java

public class GenericsTest {

  private void ContentWrapperTest () {

    ContentWrapper <ContentInfo, String> contentWrapper = new  ContentWrapper <ContentInfo, String> ();
    Envelope <ContentInfo, String> envelope = null;

    envelope = contentWrapper.wrapContent(ContentAlwaysValid.class, new String("I am a message"));
    System.out.println("Content is " + (envelope.getContentInfo().isContentValid() ? "valid" : "invalid") + ".");    

    envelope = contentWrapper.wrapContent(ContentNeverValid.class, new String("I am a message"));
    System.out.println("Content is " + (envelope.getContentInfo().isContentValid() ? "valid" : "invalid") + ".");    

  }

}

compiles and runs fine. However, this test:

    envelope = contentWrapper.wrapContent(ContentSometimesValid.class, new String("I am a message"));
    System.out.println("Content is " + (envelope.getContentInfo().isContentValid() ? "valid" : "invalid") + ".");

compiles, but during the runtime a java.lang.InstantiationException is thrown.

In some cases, instantiating an object without any meaningful content makes no sense, and developers are protecting themselves against such cases by not providing default, public constructor. Now we have a conflict of interests. Implementation deliberately does not provide a default constructor, which is necessary to instantiate an object in a reflective way. What if a constructor were provided, but with non public access modifier?

ContentSometimesValid.java

package com.application.client;

import java.io.Serializable;

import com.application.generics.ContentInfo;

public class ContentSometimesValid implements ContentInfo, Serializable {
  private static final long serialVersionUID = 2731488051426426010L;

  private boolean contentValid;

  protected ContentSometimesValid () {
    contentValid = true;
  }

  public ContentSometimesValid (boolean contentValid) {
    this.contentValid = contentValid;
  }

  public boolean isContentValid() {
    return contentValid;
  }

}

Protected constructor alone does not solve the problem – java.lang.IllegalAccessException is thrown during the runtime with a following message: Class com.application.generics.ContentWrapper can not access a member of class com.application.client.ContentSometimesValid with modifiers “protected”. Fortunately, reflection API allows code to perform actions, which would be illegal in standard code, such as invoking non public methods. In order to use this feature, we need to change our ContentWrapper class to make constructor accessible:

ContentWrapper .java

package com.application.generics;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;

public class ContentWrapper <I extends ContentInfo, C extends Serializable> {

  @SuppressWarnings("unchecked")
  public Envelope <I, C> wrapContent (Class<?> cls, C content) {

    Constructor<?> ctor = null;
    try {
      ctor = cls.getDeclaredConstructor((Class<?>[]) null);
    } catch (Exception exception) {
      throw new RuntimeException(exception);
    } 

    // make constructor accessible, if it is not already public
    if (!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) {
      ctor.setAccessible(true);
    }        

    I contentInfo = null;
    try {
      contentInfo = (I) ctor.newInstance();
    } catch (Exception exception) {
      throw new RuntimeException(exception);
    }

    return new  Envelope <I, C> (contentInfo, content);
  }

}

Modified ContentWrapper class is sufficient to do what is required, however, it is not type-safe. Since the singature of a wrapContent method:

  public Envelope <I, C> wrapContent (Class<?> cls, C content);

requires a parameter of type Class, then the following code:

  envelope = contentWrapper.wrapContent(String.class, new String("I am a message"));

compiles and obviously failes during the runtime with a java.lang.ClassCastException.

Solution to this problem comes with a java.lang.reflect.ParameterizedType interface. Using this interface is a little bit tricky since a class, within it is used, must be derived from another generic class. We can achieve it by making our ContentWrapper abstract and then instantiating it with unnamed implementations. Then, instead of passing a ContentInfo class as a parameter, we can retrieve it from ParameterizedType.

ContentWrapper .java

package com.application.generics;

import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;

public abstract class ContentWrapper <I extends ContentInfo, C extends Serializable> {

  @SuppressWarnings("unchecked")
  public Envelope <I, C> wrapContent (C content) {

    // get a class
    Class<I> cls  = (Class<I>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];

    Constructor<I> ctor = null;
    try {
      ctor = cls.getDeclaredConstructor((Class<?>[]) null);
    } catch (Exception exception) {
      throw new RuntimeException(exception);
    } 

    if (!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) {
      ctor.setAccessible(true);
    }        

    I contentInfo = null;
    try {
      contentInfo = ctor.newInstance();
    } catch (Exception exception) {
      throw new RuntimeException(exception);
    }

    return new  Envelope <I, C> (contentInfo, content);
  }

}

And on the client side:

  private void ContentWrapperTest () {

    // an abstract class is implemented
    ContentWrapper <ContentSometimesValid, String> contentWrapper = new  ContentWrapper <ContentSometimesValid, String> () {};
    Envelope <ContentSometimesValid, String> envelope = null;

    envelope = contentWrapper.wrapContent(new String("I am a message"));    

    System.out.println("Content is " + (envelope.getContentInfo().isContentValid() ? "valid" : "invalid") + ".");    

  }

Enforcing type correctness makes ContentWrapper and Envelope classes less generic, since it is not possible now to have one Envelope and one ContentWrapper instance for any ContentInfo implementation. the following code:


    Envelope <ContentInfo, String> envelope = contentWrapper.wrapContent(new String("I am a message"));

does not compile.

As seen, there is a certain trade off between both approaches and it is difficult to say which implementation is more correct. My guess is, it depends.