5

I have a function which maps a class object to an instance of this class. Basically:

 Function<Class<T>, T> fun;

I can define this function within a method but when trying to put this in a member variable, the compiler complains because the type T is unknown.

So, T is not specific to the enclosing object. It might differ from call to call. What is the correct syntax to define such a function on class level?

EDIT

I would like to clarify. I have class A

public class A {
  public <T> T get(Class<T> clazz) {...}
}

Now, I want to hand a pointer to that function to class B like this:

public class B {
  <T> Function<Class<T>, T> fun;
  
  public <T> T get(Class<T> clazz) {
    return fun.apply(clazz);
  }
}

However, the Syntax for <T> Function<Class<T>, T> fun is incorrect. Is there a way to keep the information, that fun will always retain the type T?

My current solution is

public class B {
  Function<Class<?>, ?> fun;

  public <T> void setFun(Function<Class<T>, T> fun) {
    this.fun = fun;
  }

  public <T> T get(Class<T> clazz) {
    return (T) fun.apply(clazz);
  }
}

This works (and is obviously correct via invariant) but kinda ugly because of the required cast.

Jonathan
  • 2,698
  • 24
  • 37

1 Answers1

3

In Java (and JVM) values are not polymorphic, methods are.

So correct is to define it with a method

<T> Function<Class<T>, T> fun();

Polymorphic values in Java


Regarding EDIT.

Now, I want to hand a pointer to that function to class B like this:

public class B {
  <T> Function<Class<T>, T> fun;
 
  public <T> T get(Class<T> clazz) {
    return fun.apply(clazz);
  }
}

Once again, a value (including a field) can't be polymorphic, method can.

  • So you can make fun a method

     public abstract class B {
        abstract <T> Function<Class<T>, T> fun();
    
        public <T> T get(Class<T> clazz) {
            return this.<T>fun().apply(clazz);
        }
     }
    

    You can't write setter like setFun. Its signature should be something like

     public void setFun(<T> Function<Class<T>, T> fun); // pseudocode
    

    rather than

     public <T> void setFun(Function<Class<T>, T> fun);
    

    <T> Function<Class<T>, T> is called rank-N type and it's absent in Java

    What is the purpose of Rank2Types?

    https://wiki.haskell.org/Rank-N_types

  • Alternatively, you can wrap polymorphic fun with a class (interface)

    public interface PolyFunction {
        <T> T apply(Class<T> clazz);
    }
    
    public class B {
        PolyFunction fun;
    
        public void setFun(PolyFunction fun) {
            this.fun = fun;
        }
    
        public <T> T get(Class<T> clazz) {
            return fun.apply(clazz);
        }
    }
    

    PolyFunction looks like your A so maybe you want

    public class B {
        A a;
    
        public void setA(A a) {
            this.a = a;
        }
    
        public <T> T get(Class<T> clazz) {
            return a.get(clazz);
        }
    }
    
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • The only value this can return is `null`, because `Test> t = new Test<>(); Function, String> strFn = t.fun(); Function, Integer> intFn = t.fun();` are both legal. – Andy Turner Sep 15 '20 at 13:26
  • @AndyTurner It's a function so I guess you're wrong (about `null` being the only option). For example ` Function, T> fun() { return (Class cls) -> { T t = null; try { t = cls.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } return t; }; }`. – Dmytro Mitin Sep 15 '20 at 13:35
  • I guess I am, then! – Andy Turner Sep 15 '20 at 13:46