Monday, April 25, 2016

getDeclaredConstructor on an interface?

Leave a Comment

The javadoc for Class::getDeclaredConstructor (http://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredConstructor-java.lang.Class...-) says:

Returns a Constructor object that reflects the specified constructor of the class or interface represented by this Class object. [emphasis mine]

Since you can't declare a constructor for an interface, what could it mean to return a "specified constructor" of an interface?

I tried it on Runnable.class and got NoSuchMethodException. Is there a case where getDeclaredConstructor will work on an interface? Or is this language in the javadoc just an error? Or does it mean something other than how I'm interpreting it?

3 Answers

Answers 1

A call to Class.getConstructor will result in a call to Class.privateGetDeclaredConstructors to retrieve all declared constructors. The matching constructor is selected from that list:

private Constructor<T>[] privateGetDeclaredConstructors(boolean publicOnly) {     ...     // No cached value available; request value from VM     if (isInterface()) {         @SuppressWarnings("unchecked")         Constructor<T>[] temporaryRes = (Constructor<T>[]) new Constructor<?>[0];         res = temporaryRes;     } else {         res = getDeclaredConstructors0(publicOnly);     }     ...     return res; } 

(I removed part of the code which deals with cached constructors).

So for interfaces the constructor list is always empty and a NoSuchMethodException will always be thrown.

Answers 2

I don't consider this an error in javadoc. Class object can represent class or interface, there is no error in that statement.

If you're using reflection and explicitly ask for specific elements, you have to make sure that elements with the identification specified in the reflective calls exist. If you're asking for a specific constructor that does not exist in the provided Class, you'll get NoSuchMethodException be it a class for an interface, primitive type, array, void or simply a class that does not declare such a constructor.

Above I emphasised the word specific. For example, in a similar method that returns all constructors for a Class (Class::getDeclaredConstructors) interfaces are handled appropriately:

Returns an array of Constructor objects reflecting all the constructors declared by the class represented by this Class object. These are public, protected, default (package) access, and private constructors. The elements in the array returned are not sorted and are not in any particular order. If the class has a default constructor, it is included in the returned array. This method returns an array of length 0 if this Class object represents an interface, a primitive type, an array class, or void.

Answers 3

This is the use case I can think of: on a reflection-based application, a method receives a Class argument. The method needs to construct an instance of the given class, so it checks for the declared constructors. This is a simplified example that just prints the constructors on four different use cases:

  1. A class instance invoking getClass()
  2. An interface instance invoking getClass()
  3. An actual ClassX.class
  4. An actual InterfaceY.class

    public class Testctor {      public static void main(String[] args) {         new Testctor().testGetDeclCtors();     }      public void testGetDeclCtors() {         Class1 c1 = new Class1(4);         printClassInfo(c1.getClass());          I2 i2 = c1;         printClassInfo(i2.getClass());          printClassInfo(Class1.class);          printClassInfo(I2.class);     }      private void printClassInfo(Class<?> claz) {         Constructor<?> ctor = null;         try {             ctor = claz.getDeclaredConstructor();         } catch (NoSuchMethodException | SecurityException e) {         }         System.out.println(claz+": "+ctor);     } }  interface I2 { }  class Class1 implements I2 {     public Class1() {     }      public Class1(int x) {     } } 

This prints these results:

class test.Class1: public test.Class1() class test.Class1: public test.Class1() class test.Class1: public test.Class1() interface test.I2: null 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment