1

I have this code:

    public <V> V getPropertiesByObject(V sample) throws TimeoutException {
//settings fields using reflaction
        return sample;
    }

which I call like this:

MyClass a = getPropertiesByObject(new MyClass());

only because I don't know how to construct an instance otherwise.

I would prefer:

public <V> V getPropertiesByObject(Class<V> sample) throws TimeoutException {
//how to create a new V instance?
    return result;
}

Is there a way to refactor my original code?

Elad Benda2
  • 13,852
  • 29
  • 82
  • 157

3 Answers3

2

You can use reflection - here's a basic implementation that shows one way:

public <V> V getPropertiesByObject(Class<V> clazz, Object... params) throws TimeoutException {
    Class<?>[] paramClasses = new Class<?>[params.length];
    for (int i =0; i < params.length; i++)
        paramClasses[i] = params[i].getClass(); 
    V result = clazz.getConstructor(paramClasses).newInstance((Object[])params);
    return result;
}

The parameter params, which may be empty, are the parameters to pass to the constructor corresponding to those parameters. This code won't handle null values. Here's how you might call it:

String str = getPropertiesByObject(String.class); // blank String
Integer i = getPropertiesByObject(Integer.class, "1"); // 1
Bohemian
  • 412,405
  • 93
  • 575
  • 722
  • You are losing the benefit from generics with that implementation. For example "String string = getPropertiesByObject(String.class, new Object())" will not report an error at compile time but will crash at runtime. The whole point of generics is to guarantee correctness of calls at compile time and prevent runtime errors. – vap78 May 11 '15 at 15:49
  • @vap78 of course - reflection bypasses not only generics, but all static type checks. You can protect yourself from runtime errors by writing unit tests that exercise your reflective code - although not strictly *compile time* checking, *build time* checking is close enough (since your run all unit tests when you compile your code). The main downside is you don't get any help from IDEs and this kind of code is harder for read. – Bohemian May 11 '15 at 15:57
  • what I mean that with the suggested changes the usage of generics makes little sense. The method signature could be "Object getPropertiesByObject(Class clazz, Object... params)" and there will be very little difference - the caller will anyway have to watch to pass parameters that match the constructor of the given class. – vap78 May 11 '15 at 18:10
  • @vap78 yes, but if the method is typed, as least the calling code can infer the type so it's quite useful. You're right that typing the method doesn't make its implementation any safer ("the gloves are off" when you use reflection) – Bohemian May 11 '15 at 23:39
1

If V has a parameterless constructor, you can use Class<V>::newInstance().

public <V> V getPropertiesByObject(Class<V> sample) throws TimeoutException {
    V result = sample.newInstance();
    // stuff on V
    return result;
}

Of course, you will use it like this: MyClass a = getPropertiesByObject(MyClass.class)

If you have to specify some parameters, you can do something like:

public <V> V getPropertiesByObject(Class<V> sample, Object... params) throws TimeoutException {
    V result = sample.newInstance();
    result.param0 = params[0];
    result.param1 = params[1];
    // etc  
    return result;
}

But in both case, MyClass must have a parameterless constructor. If you can't edit MyClass, you should use the Bohemian's answer using Constructor.

NiziL
  • 5,068
  • 23
  • 33
  • and if no parameterless ctor? – Elad Benda2 May 11 '15 at 12:36
  • @user1065869 Well, you can add one and specify parameters like in my edited answer, or use `Class::getConstrutor(Class>... parameterTypes)` as specified in the Bohemian's answer :) – NiziL May 11 '15 at 12:53
0

So you want to create a method that can create an instance of ANY possible class and set ANY possible fields to the instance?

This is quite hard. Frameworks like GSON have the same issue and they either require that you have a no-argument constructor in the beans used, or provide a way to create instances for a certain class (see Is default no-args constructor mandatory for Gson?).

Else for your specific case - if you know the set of classes that you are going to instantiate then you can implement a factory class that creates instances based on a class object with some kind of if-else-if-else... structure.

Community
  • 1
  • 1
vap78
  • 1,029
  • 2
  • 11
  • 26