I have a java class Jistthat has two fields, public final Object type and public Object[] content. In the constructor for Jist I want to take in an Object and get its type, then initialize content with some number of empty slots of that type. This is one of the several solutions that I have tried, and the error that I am currently receiving is in the actual initialization of the array:
public class Jist{
public final Object type;
public Object[] content;
public Jist(Object type){
this.type = type.getClass();
class myType extends Jist{
Class c = super.type.getClass();
public Class getType(){
return c;
}
};
content = new myType.getType()[4];
}
}
Make Jist generic on some type T. Pass the Class<T> in the constructor. Use Array.newInstance(Class<?>, int) like
public class Jist<T> {
public final Class<T> type;
public T[] content;
public Jist(Class<T> cls) {
this.type = cls;
content = (T[]) Array.newInstance(type, 4);
}
}
Yes, you can create new array instance using type Class.
Class<?> myTypeClass = myType.getType();
int arraySize = 10;
Object array = Array.newInstance(myTypeClass, arraySize);
https://docs.oracle.com/javase/tutorial/reflect/special/arrayInstance.html
Why do suppliers only support no-arg constructors?
If the default constructor is present, I can do this:
create(Foo::new)
But if the only constructor takes a String, I have to do this:
create(() -> new Foo("hello"))
But, a 1-arg constructor for T that takes a String is compatible with Function<String,T>:
Function<String, Foo> fooSupplier = Foo::new;
Which constructor is selected is treated as an overload selection problem, based on the shape of the target type.
That's just a limitation of the method reference syntax -- that you can't pass in any of the arguments. It's just how the syntax works.
If you like method references so much, you can write a bind method by yourself and use it:
public static <T, R> Supplier<R> bind(Function<T,R> fn, T val) {
return () -> fn.apply(val);
}
create(bind(Foo::new, "hello"));
The Supplier<T> interface represents a function with a signature of () -> T, meaning it takes no parameters and returns something of type T. Method references that you provide as arguments must follow that signature in order to be passed in.
If you want to create a Supplier<Foo> that works with the constructor, you can use the general bind method that #Tagir Valeev suggests, or you make a more specialized one.
If you want a Supplier<Foo> that always uses that "hello" String, you could define it one of two different ways: as a method or a Supplier<Foo> variable.
method:
static Foo makeFoo() { return new Foo("hello"); }
variable:
static Supplier<Foo> makeFoo = () -> new Foo("hello");
You can pass in the method with a method reference(create(WhateverClassItIsOn::makeFoo);), and the variable can be passed in simply using the name create(WhateverClassItIsOn.makeFoo);.
The method is a little bit more preferable because it is easier to use outside of the context of being passed as a method reference, and it's also able to be used in the instance that someone requires their own specialized functional interface that is also () -> T or is () -> Foo specifically.
If you want to use a Supplier that can take any String as an argument, you should use something like the bind method #Tagir mentioned, bypassing the need to supply the Function:
Supplier<Foo> makeFooFromString(String str) { return () -> new Foo(str); }
You can pass this as an argument like this: create(makeFooFromString("hello"));
Although, maybe you should change all the "make..." calls to "supply..." calls, just to make it a little clearer.
Why do suppliers only work with no-arg constructors?
Because a 1-arg constructor is isomorphic to a SAM interface with 1 argument and 1 return value, such as java.util.function.Function<T,R>'s R apply(T).
On the other hand Supplier<T>'s T get() is isomorphic to a zero arg constructor.
They are simply not compatible. Either your create() method needs to be polymorphic to accept various functional interfaces and act differently depending on which arguments are supplied or you have to write a lambda body to act as glue code between the two signatures.
What is your unmet expectation here? What should happen in your opinion?
Pair the Supplier with a FunctionalInterface.
Here's some sample code I put together to demonstrate "binding" a constructor reference to a specific constructor with Function and also different ways of defining and invoking the "factory" constructor references.
import java.io.Serializable;
import java.util.Date;
import org.junit.Test;
public class FunctionalInterfaceConstructor {
#Test
public void testVarFactory() throws Exception {
DateVar dateVar = makeVar("D", "Date", DateVar::new);
dateVar.setValue(new Date());
System.out.println(dateVar);
DateVar dateTypedVar = makeTypedVar("D", "Date", new Date(), DateVar::new);
System.out.println(dateTypedVar);
TypedVarFactory<Date, DateVar> dateTypedFactory = DateVar::new;
System.out.println(dateTypedFactory.apply("D", "Date", new Date()));
BooleanVar booleanVar = makeVar("B", "Boolean", BooleanVar::new);
booleanVar.setValue(true);
System.out.println(booleanVar);
BooleanVar booleanTypedVar = makeTypedVar("B", "Boolean", true, BooleanVar::new);
System.out.println(booleanTypedVar);
TypedVarFactory<Boolean, BooleanVar> booleanTypedFactory = BooleanVar::new;
System.out.println(booleanTypedFactory.apply("B", "Boolean", true));
}
private <V extends Var<T>, T extends Serializable> V makeVar(final String name, final String displayName,
final VarFactory<V> varFactory) {
V var = varFactory.apply(name, displayName);
return var;
}
private <V extends Var<T>, T extends Serializable> V makeTypedVar(final String name, final String displayName, final T value,
final TypedVarFactory<T, V> varFactory) {
V var = varFactory.apply(name, displayName, value);
return var;
}
#FunctionalInterface
static interface VarFactory<R> {
// Don't need type variables for name and displayName because they are always String
R apply(String name, String displayName);
}
#FunctionalInterface
static interface TypedVarFactory<T extends Serializable, R extends Var<T>> {
R apply(String name, String displayName, T value);
}
static class Var<T extends Serializable> {
private String name;
private String displayName;
private T value;
public Var(final String name, final String displayName) {
this.name = name;
this.displayName = displayName;
}
public Var(final String name, final String displayName, final T value) {
this(name, displayName);
this.value = value;
}
public void setValue(final T value) {
this.value = value;
}
#Override
public String toString() {
return String.format("%s[name=%s, displayName=%s, value=%s]", getClass().getSimpleName(), this.name, this.displayName,
this.value);
}
}
static class DateVar extends Var<Date> {
public DateVar(final String name, final String displayName) {
super(name, displayName);
}
public DateVar(final String name, final String displayName, final Date value) {
super(name, displayName, value);
}
}
static class BooleanVar extends Var<Boolean> {
public BooleanVar(final String name, final String displayName) {
super(name, displayName);
}
public BooleanVar(final String name, final String displayName, final Boolean value) {
super(name, displayName, value);
}
}
}
When looking for a solution to the parametrized Supplier problem, I found the above answers helpful and applied the suggestions:
private static <T, R> Supplier<String> failedMessageSupplier(Function<String,String> fn, String msgPrefix, String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> fn.apply(msgString);
}
It is invoked like this:
failedMessageSupplier(String::new, msgPrefix, customMsg);
Not quite satisfied yet with the abundant static function parameter, I dug further and with Function.identity(), I came to the following result:
private final static Supplier<String> failedMessageSupplier(final String msgPrefix, final String ... customMessages) {
final String msgString = new StringBuilder(msgPrefix).append(" - ").append(String.join("\n", customMessages)).toString();
return () -> (String)Function.identity().apply(msgString);
};
Invocation now without the static function parameter:
failedMessageSupplier(msgPrefix, customMsg)
Since Function.identity() returns a function of the type Object, and so does the subsequent call of apply(msgString), a cast to String is required - or whatever the type, apply() is being fed with.
This method allows for e. g. using multiple parameters, dynamic string processing, string constants prefixes, suffixes and so on.
Using identity should theoretically also have a slight edge over String::new, which will always create a new string.
As Jacob Zimmerman already pointed out, the simpler parametrized form
Supplier<Foo> makeFooFromString(String str1, String str2) {
return () -> new Foo(str1, str2);
}
is always possible. Whether or not this makes sense in a context, depends.
As also described above, static Method reference calls require the corresponding method's number and type of return / parameters to match the ones expected by the function-consuming (stream) method.
If you have a constructor for new Klass(ConstructorObject) then you can use Function<ConstructorObject, Klass> like this:
interface Interface {
static Klass createKlass(Function<Map<String,Integer>, Klass> func, Map<String, Integer> input) {
return func.apply(input);
}
}
class Klass {
private Integer integer;
Klass(Map<String, Integer> map) {
this.integer = map.get("integer");
}
public static void main(String[] args) {
Map<String, Integer> input = new HashMap<>();
input.put("integer", 1);
Klass klazz = Interface.createKlass(Klass::new, input);
System.out.println(klazz.integer);
}
}
If I have a class with a generic like this:
public class ResponseSimple<T> {
private Map<String, Collection<String>> headers;
private int status;
private T body;
}
Then,in other class I have a method which I need to use an instance of this class, but the method passes by param a java.lang.reflect.Type and it's overrided so I can't change the any of the method (name, signature..):
public class ResponseEncoder extends GsonDecoder {
public ResponseEncoder() {
super();
}
#Override
public Object decode(Response response, Type type) throws IOException
{
//How assign type T using type param??
//¿ResponseSimple<T> responseSimple = new ResponseSimple();?
return null;
}
}
How could I assign the generic type T using the param type (java.lang.reflect.Type)?
I would suggest something like this:
#Override
public <T> T decode(Response response, Class<T> type) throws IOException
{
//How assign type T using type param??
ResponseSimple<T> response = new ResponseSimple<T>();
return response;
}
Then use decode as follows:
.decode(response, NameOfClass.class)
Edit:
If you need to extend your class you could use a static helper function:
public static <T> ResponseSimple<T> createResponse(Class<T> clazz)
{
return new ResponseSimple<>();
}
And use it like this:
public class ResponseEncoder extends GsonDecoder {
public ResponseEncoder() {
super();
}
#Override
public Object decode(Response response, Type type) throws IOException
{
Class<?> clazz = (Class<?>) type;
ResponseSimple<?> response = createResonse(clazz);
return null;
}
}
I hope I understood your question correctly.
To create a new instance of your generic class you need to infer the correct type arguments like this if you want your ResponseSimple<T> to contain java.lang.reflect.Type:
ResponseSimple<Type> response = new ResponseSimple<>();
So, inbetween the <> you need to add the name of the class you want to use.
Also have a look at this: https://docs.oracle.com/javase/tutorial/java/generics/types.html
//EDIT:
As you said you want to infer the type arguments dynamically, what you did works fine for me. The only thing is that you forgot the diamond operator:
#Override
public Object decode(Response response, T type) throws IOException
{
ResponseSimple<T> response = new ResponseSimple<>(); //<> in the second part
return null;
}
I have a class for example
public class Example<T> {...}
I would like to instantiate class Example with a specific type class which I know. Pseudocode would look something like that
public Example<T> createTypedExample(Class exampleClass, Class typeClass) {
exampleClass.newInstance(typeClass); // made-up
}
So that this would give me same result
Example<String> ex = new Example<String>();
ex = createTypedExample(Example.class, String.class);
Is it possible in Java?
Since, the return type i.e. the class of the new instance is fixed; there's no need to pass it to the method. Instead, add a static factory method to your Example class as
public class Example<T> {
private T data;
static <T> Example<T> newTypedExample(Class<T> type) {
return new Example<T>();
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
Now, here's how you would create generic Example instances.
// String
Example<String> strTypedExample = Example.newTypedExample(String.class);
strTypedExample.setData("String Data");
System.out.println(strTypedExample.getData()); // String Data
// Integer
Example<Integer> intTypedExample = Example.newTypedExample(Integer.class);
intTypedExample.setData(123);
System.out.println(intTypedExample.getData()); // 123
Can I create an instance of a generic class without knowing its type parameter? I have only 'class' type, which have been obtained from the Class.forName (classname);
//generic class
public class MappedField<T> extends Field {
private T value;
private String sourceName;
public MappedField(String name, String sourceName, FieldType type){
super(name, type);
this.sourceName = sourceName;
}
public MappedField(String name, String sourceName, FieldType type, boolean key){
super(name, type, key);
this.sourceName = sourceName;
}
}
//here's my situation
String columnName = field.getSourceName() != null ? field.getSourceName() : field.getName();
Class c = Class.forName(field.getType().getFullClassName());
//I need like that if `field.getType() = "Integer"`: `MappedField<Integer> objField = new MappedField<Integer>(field)`;
MappedField objField = new MappedField(field);
objField.setValue(getField(c, rset, columnName));
object.getValues().add(objField);
EDIT:
It's like I have "java.lang.Integer" and want get MappedField
The Generic type will erased from the classes and interfaces on successful compilation.On runtime it is not possible to detect the generic type.
Refer the link:
Use reflection to create a generic parameterized class in Java