0

I want to define a function that creates different type objects that share the same base class. I'd like to pass in the object type and have the function creating the object and then modifying its attributes. The problem is that the main class from which all these objects are created, does not have the object's attributes so the code fails to compile.

Example:

public void new_generic_report(Class report_class, String report_name) {

    Report new_report = this.reportManager.createReport(report_class);
    new_report.set_name(report_name);

}

Calling new_generic_report(GreenReport.class, "green_report"); fails because new_report is of the class Report instead of GreenReport so it does not have the .set_name method.

I know I could implement the .set_name method (and other common methods) in the main Report class but I am writing code to interface with an API that I cannot modify.

4
  • There are many Report sub types. Do I need to write a large if/else (or switch) with if( new_report instanceof a_report_class) {} else if ... ? Commented Jan 26, 2018 at 12:57
  • Do you mean that reportManager.createReport(report_class) is used by an api that returns a Report element ? In that case you don't have control over the returned type. Commented Jan 26, 2018 at 13:02
  • @Laurent B The .createReport method returns a report of the correct sub-type. However I declared it as a the main class Report because I do not know how to create the object with dynamic type based on report_class. Commented Jan 26, 2018 at 13:13
  • In that case if you control the returned type you can avoid doing multiple if else using a parent class Commented Jan 26, 2018 at 13:20

2 Answers 2

1

If you are sure that createReport returns an instance of the correct class you can just do a cast:

((SpecialClass)new_report).set_name(report_name);

An alternative is to use reflection:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {
    static class Base {};
    static class Child extends Base {
        public void setName(final String name) {
            System.out.println("setName("+name+")");
        }
    }


    public static void main(String[] args) {
        new Test().new_generic_report(Child.class, "Testname");
    }


    public void new_generic_report(final Class clazz, final String name) {
        Base base = createBase(clazz);

        try {
            Method m = clazz.getMethod("setName", String.class);
            m.invoke(base, name);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private Base createBase(Class report_class) {
        return new Child();
    }
}

Of course this only works, if the returned instance implements the method.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you @Daniel. The method .createReport does return an instance of the correct class. There are several "SpecialClass" so I guess I need to pass it as an argument. However, when I cast it as ((report_class)new_report).set_name(report_name); the compiler says that it cannot find report_class.
The second approach you suggest makes sense, but I'd like to make the first one work which is much shorter.
It's only shorter because I didn't add the different cases. You'd have to do something like: if (new_report instanceof(SpecialClassA)) ((SpecialClassA)new_report).methodCall(...); But I have to admit, that at first glance it still is the cleaner and safer variant.
1

Create a parent class for your report for instance :

public abstract class NamedReport extends Report
{
    public abstract void setName(String name);
}


class GreenReport extends NamedReport {

    @Override
    public void setName(String name) {

    } 
}

Then simply cast your class in your method :

public void new_generic_report(Class report_class, String report_name) {

    Report new_report = this.reportManager.createReport(report_class);
    if (new_report  instanceof NamedReport)
    {
        ((NamedReport)new_report).set_name(report_name);
    }
}

2 Comments

If I have GreenReport, BlueReport, BrownReport, ..., am I supposed to create NamedReport extensions for all of them?
Yes somehow you do unless you use a composite object that contains both your report and the name variable

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.