Asked  7 Months ago    Answers:  5   Viewed   34 times

Is it possible to get the type of a generic parameter?

An example:

public final class Voodoo {
    public static void chill(List<?> aListWithTypeSpiderMan) {
        // Here I'd like to get the Class-Object 'SpiderMan'
        Class typeOfTheList = ???;
    }

    public static void main(String... args) {
        chill(new ArrayList<SpiderMan>());
    }
}

 Answers

29

One construct, I once stumbled upon looked like

Class<T> persistentClass = (Class<T>)
   ((ParameterizedType)getClass().getGenericSuperclass())
      .getActualTypeArguments()[0];

So there seems to be some reflection-magic around that I unfortunetly don't fully understand... Sorry.

Tuesday, June 1, 2021
 
christina
answered 7 Months ago
90

I have found a way of doing it for serializable lambdas. All my lambdas are serializable, to that works.

Thanks, Holger, for pointing me to the SerializedLambda.

The generic parameters are captured in the lambda's synthetic static method and can be retrieved from there. Finding the static method that implements the lambda is possible with the information from the SerializedLambda

The steps are as follows:

  1. Get the SerializedLambda via the write replacement method that is auto-generated for all serializable lambdas
  2. Find the class that contains the lambda implementation (as a synthetic static method)
  3. Get the java.lang.reflect.Method for the synthetic static method
  4. Get generic types from that Method

UPDATE: Apparently, this does not work with all compilers. I have tried it with the compiler of Eclipse Luna (works) and the Oracle javac (does not work).


// sample how to use
public static interface SomeFunction<I, O> extends java.io.Serializable {

    List<O> applyTheFunction(Set<I> value);
}

public static void main(String[] args) throws Exception {

    SomeFunction<Double, Long> lambda = (set) -> Collections.singletonList(set.iterator().next().longValue());

    SerializedLambda sl = getSerializedLambda(lambda);      
    Method m = getLambdaMethod(sl);

    System.out.println(m);
    System.out.println(m.getGenericReturnType());
    for (Type t : m.getGenericParameterTypes()) {
        System.out.println(t);
    }

    // prints the following
    // (the method) private static java.util.List test.ClassWithLambdas.lambda$0(java.util.Set)
    // (the return type, including *Long* as the generic list type) java.util.List<java.lang.Long>
    // (the parameter, including *Double* as the generic set type) java.util.Set<java.lang.Double>

// getting the SerializedLambda
public static SerializedLambda getSerializedLambda(Object function) {
    if (function == null || !(function instanceof java.io.Serializable)) {
        throw new IllegalArgumentException();
    }

    for (Class<?> clazz = function.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
        try {
            Method replaceMethod = clazz.getDeclaredMethod("writeReplace");
            replaceMethod.setAccessible(true);
            Object serializedForm = replaceMethod.invoke(function);

            if (serializedForm instanceof SerializedLambda) {
                return (SerializedLambda) serializedForm;
            }
        }
        catch (NoSuchMethodError e) {
            // fall through the loop and try the next class
        }
        catch (Throwable t) {
            throw new RuntimeException("Error while extracting serialized lambda", t);
        }
    }

    throw new Exception("writeReplace method not found");
}

// getting the synthetic static lambda method
public static Method getLambdaMethod(SerializedLambda lambda) throws Exception {
    String implClassName = lambda.getImplClass().replace('/', '.');
    Class<?> implClass = Class.forName(implClassName);

    String lambdaName = lambda.getImplMethodName();

    for (Method m : implClass.getDeclaredMethods()) {
        if (m.getName().equals(lambdaName)) {
            return m;
        }
    }

    throw new Exception("Lambda Method not found");
}
Tuesday, June 1, 2021
 
barden
answered 7 Months ago
69

I would write a wrapper around the int[][] data and call it a Matrix class. Then write a method getSubMatrix(x, y, rows, cols). This is a simple Matrix class:

static class Matrix {
    int[][] data;
    int x, y, columns, rows;

    public Matrix(int[][] data) {
        this(data, 0, 0, data.length, data[0].length);
    }

    private Matrix(int[][] data, int x, int y, int columns, int rows) {
        this.data = data;
        this.x = x;
        this.y = y;
        this.columns = columns;
        this.rows = rows;
    }

    public Matrix getSubMatrix(int x, int y, int columns, int rows) {
        return new Matrix(data, this.x + x , this.y + y, columns, rows);
    }

    public String toString() {

        StringBuffer sb = new StringBuffer();

        for (int i = y; i < x + rows; i++) {
            for (int j = x; j < x + columns; j++)
                sb.append(data[i][j]).append(" ");

            sb.append("n");
        }
        sb.setLength(sb.length() - 1);

        return sb.toString();
    }
}

This test program...:

public static void main(String[] args) throws IOException {

    int[][] testData = new int[10][10];

    for (int i = 0; i < testData.length; i++) 
        for (int j = 0; j < testData[i].length; j++)
            testData[i][j] = 100 + i + j;

    Matrix full = new Matrix(testData);

    System.out.println("Full test matrix:");
    System.out.println(full);

    System.out.println();

    System.out.println("Part of the matrix:");
    System.out.println(full.getSubMatrix(3, 3, 3, 3));

}

...prints:

Full test matrix:
100 101 102 103 104 105 106 107 108 109 
101 102 103 104 105 106 107 108 109 110 
102 103 104 105 106 107 108 109 110 111 
103 104 105 106 107 108 109 110 111 112 
104 105 106 107 108 109 110 111 112 113 
105 106 107 108 109 110 111 112 113 114 
106 107 108 109 110 111 112 113 114 115 
107 108 109 110 111 112 113 114 115 116 
108 109 110 111 112 113 114 115 116 117 
109 110 111 112 113 114 115 116 117 118 

Part of the matrix:
106 107 108 
107 108 109 
108 109 110 
Thursday, August 5, 2021
 
Kemrop
answered 4 Months ago
58

You're in luck. I actually had to do something very similar a few weeks ago.

For a detailed explanation see the above blog post, but basically the general idea is to reflect the type and manually invoke the method with an explicit set of parameters.

typeof(MyClass).GetMethod("Foo").MakeGenericMethod(new[] { param.GetType() }).Invoke(null, new[] { param });

It's not very type safe, but it does exactly what you're looking for.

class Program
{

    static void Main(string[] args)
    {
        object str = "Hello World";
        object num = 5;
        object obj = new object();

        Console.WriteLine("vartvaluettFoo() TypetCallFoo() Type");
        Console.WriteLine("-------------------------------------------------------");
        Console.WriteLine("{0}t{1}t{2}t{3}", "str", str, MyClass.Foo(str), MyClass.CallFoo(str));
        Console.WriteLine("{0}t{1}tt{2}t{3}", "num", num, MyClass.Foo(num), MyClass.CallFoo(num));
        Console.WriteLine("{0}t{1}t{2}t{3}", "obj", obj, MyClass.Foo(obj), MyClass.CallFoo(obj));
    }

}

class MyClass
{
    public static Type Foo<T>(T param)
    {
        return typeof(T);
    }

    public static Type CallFoo(object param)
    {
        return (Type)typeof(MyClass).GetMethod("Foo").MakeGenericMethod(new[] { param.GetType() }).Invoke(null, new[] { param });
    }

}

Output

   var     value           Foo() Type      CallFoo() Type
   -------------------------------------------------------
   str     Hello World     System.Object   System.String
   num     5               System.Object   System.Int32
   obj     System.Object   System.Object   System.Object
Saturday, August 14, 2021
 
redrom
answered 4 Months ago
90

Generics that have concrete types part of declarations (methods, fields, classes, arguments) are retained.

So you can obtain the types from this declaration

 public List<String> toString(List<Foo> foos) { .. }

But you can't from this code:

public List<E> transform(List<E> list) {
  // E is not accessible at runtime
}
Monday, November 15, 2021
 
George Shaw
answered 2 Weeks ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share