-2

I want to have a class to run other classes in java, like constructor parameterized with a class to run that class later on, similar to this

class MyClass{
    Class classToRun;
    public MyClass(Class c) {
        super();
        this.classToRun = c;
    }
    public void runClass(){
        classToRun.someStaticMethod();
    }
}

where classToRun possible classes doesn't have a common ancestor, but all have method someStaticMethod, and have no idea about MyClass, which runs them.

But there are problems, like inner classes cannot have static methods, classes cannot be cast Class, etc.

There are solutions for parameterized with class methods, like

How do I pass a class as a parameter in Java?

Passing a class as an argument to a method in java

but not for constructors.

What is the proper solution to do this?

WebComer
  • 1,131
  • 2
  • 19
  • 31
  • 1
    You cannot call `classToRun.someStaticMethod();` anyways if `classToRun` is a `Class`. You need reflection code to call that function. – f1sh Jan 06 '22 at 12:37
  • Also, please read: [What is a raw type and why shouldn't we use it?](https://stackoverflow.com/questions/2770321/what-is-a-raw-type-and-why-shouldnt-we-use-it) – Turing85 Jan 06 '22 at 12:39
  • @Turing85 You are free to put down a better working solution instead of a non working frame of idea, if you can. – WebComer Jan 06 '22 at 12:43
  • 1
    WebComer without putting some type of limitation on the classes, how would you assume you can guarantee that method is there? – Stultuske Jan 06 '22 at 12:44
  • 2
    @WebComer The current design is broken. There is no proper (i.e. clean) solution to the problem since - 1. there is no common superclass of those classes and - 2. even if there were, static methods are not inherited. I recommend to reevaluate the overall design. – Turing85 Jan 06 '22 at 12:48
  • @ Stultuske If a class don't have someStaticMethod, exception at run time may be thrown, that is just ok. – WebComer Jan 06 '22 at 12:48
  • Does this answer your question? [Passing a class as argument to a method, then calling static methods](https://stackoverflow.com/questions/65791611/passing-a-class-as-argument-to-a-method-then-calling-static-methods) – Joe Jan 06 '22 at 12:51
  • @Turing85 I really want a better solution instead broken one. The idea is to parameterize class with another with someStaticMethod. – WebComer Jan 06 '22 at 12:52
  • @Joe The class to run doesn't inherit BaseClass, it just contains static method someStaticMethod. That's all. Beside of this, the answer you have mentioned is not a complete working solution for the problem. If you believe, it is a solution, you may put a full working proof code. So far, only Joop Eggen's solution fits the requirements. – WebComer Jan 06 '22 at 15:19
  • @Turing85 For the task, no common superclass of those classes to run is one of the main requirements. – WebComer Jan 06 '22 at 15:42

4 Answers4

2

Use lambdas and pass the method reference: they match on the method signature. For void someStaticMethod() you can use Runnable.

class MyClass{
    private final Runnable methodToRun;
    public MyClass(Runnable someStaticMethod) {
        methodToRun = someStaticMethod;
    }
    public void runClass(){
        methodToRun.run();
    }
}

new MyClass(SomeClass::someStaticMethod).runClass();

You cannot enforce that the method passed has the right name, but looks even neater IMHO.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • The classToRun shouldn't be limited to Runnable interface. – WebComer Jan 06 '22 at 13:11
  • To call the methods you must have a method signature, for `String someStaticMethod (double)` use `DoubleFunction` and `return methodToRun.apply(3.14);` – Joop Eggen Jan 06 '22 at 13:15
  • 2
    The class itself does not need to implement the interface, it is structurally matched (method signature). – Joop Eggen Jan 06 '22 at 13:17
  • @ Joop Eggen Yes, thanks, very neat solution, btw! But I would name methodToRun like runnableForMethodToRun, as it is a Runnable instance. It's a bit weird for me, a static method turned to be a Runnable, and Java did swallow that! ))) Yeah, I know, may be reflection works, but looks like a kind of a hack for me))) – WebComer Jan 06 '22 at 14:33
  • 1
    It is the java way to have function and method "pointers." So it fits your requirements. And I agree: good naming is important. – Joop Eggen Jan 06 '22 at 14:37
  • It fits the requirements, and it's coool!))) – WebComer Jan 06 '22 at 14:55
0

You need to understand what generics are.

interface

public interface SomeInterface {
    void someStaticMethod();
}

use

class MyClass<T extends SomeInterface>{
    T classToRun;
    public MyClass(T c) {
        super();
        this.classToRun = c;
    }
    public void runClass(){
        classToRun.someStaticMethod();
    }
}
lzwei
  • 58
  • 5
  • The problem is the classToRun classes do not extend a common SomeInterface, just have someStaticMethod to be run. – WebComer Jan 06 '22 at 12:46
  • 1
    @WebComer Then you have to use reflection or annotation. – lzwei Jan 06 '22 at 12:49
  • Reflection looks like a better idea, may be you can fix your solution. Just guess about your words about annotation, but you may put down your annotation solution as well. – WebComer Jan 06 '22 at 12:59
0

As 2 of 3 answers were not to the point, I decided to publish fixed versions of both answers as far as they can be fixed.

The f1sh version from the above should like follows:

public class ClassToRunOthers {
    Class classToRun;
    public ClassToRunOthers(Class c) {
        this.classToRun = c;
    }
    public void runClass() throws Exception {
        Optional<Method> method = Arrays.stream(classToRun.getDeclaredMethods()).filter(m -> m.getName().equals("someStaticMethod")).findFirst();
        if(!method.isPresent()) {
            throw new RuntimeException();
        }
        method.get().invoke(null);
    }
    public static void main(String[] args) throws Exception {
        ClassToRunOthers mc = new ClassToRunOthers(SomeClass.class);
        mc.runClass();
    }
}

class SomeClass {
    static void someStaticMethod() {
        System.out.println("test");
    }
}

The zwei solution above can not be fixed without reflection, as generics is not to the point. Evan if you try to parametrize not with SomeInerface (because SomeClass does not extend a common SomeInterface), but with Object, it is still won't solve the problem:

public class MyClass<T extends Object> {
    T classToRun;

    public MyClass(T c) {
        super();
        this.classToRun = c;
    }

    public void runClass() {
        //        classToRun.someStaticMethod(); // Cannot resolve method 'someStaticMethod' in 'T'
    }

    public static void main(String[] args) {
        MyClass mc = new MyClass(SomeClass.class);
    }
}

class SomeClass {
    static void someStaticMethod() {
        System.out.println("test");
    }
}

This can be fixed like the above, via reflection. I believe, it can be done with annotations in some elegant way, and may be someone will share us with such a solution or I will do it by myself as time permits. By now for myself, a solution with saving class name in the String in constructor next day after the question been asked did the trick.

Mario Petrovic
  • 7,500
  • 14
  • 42
  • 62
WebComer
  • 1,131
  • 2
  • 19
  • 31
-1

You will have to use reflection if you want to execute a method when you only have the Class instance.

In the code below, runClass finds the method of the class using it's name as a String, then executes it. This code assumes that the method is static, also ignoring any Exception handling.

The following code prints "test":

class MyClass {
    Class classToRun;
    public MyClass(Class c) {
        this.classToRun = c;
    }
    public void runClass() throws Exception {
        Optional<Method> method = Arrays.stream(classToRun.getDeclaredMethods()).filter(m -> m.getName().equals("someStaticMethod")).findFirst();
        if(!method.isPresent()) {
            throw new RuntimeException();
        }
        method.get().invoke(null);
    }
}


class Main {
    public static void main(String[] args) throws Exception {
        MyClass mc = new MyClass(Main.class);
        mc.runClass();
    }

    static void someStaticMethod() {
        System.out.println("test");
    }
}
f1sh
  • 11,489
  • 3
  • 25
  • 51
  • The code is working, but MyClass shouldn't be static inner one, it's in another package. – WebComer Jan 06 '22 at 13:10
  • that doesnt make a difference. My code is just an example, the code in `runClass` works with any class as long as the static method is not private. – f1sh Jan 06 '22 at 13:18
  • @WebComer edited the code to make that change clear – f1sh Jan 06 '22 at 13:25
  • SomeClassToRun (you named it Main) with someStaticMethod shouldn't contain MyClass. It's not even know about it. – WebComer Jan 06 '22 at 15:05
  • You just didn't bother to read nor question requirements, nor your code in your solution. The requirement is SomeClass, which you named Main, should have no idea about MyClass, that run it. But the code you provided have even instance of MyClass in Main code, which is inappropriate, so it is not a solution for the problem stated. – WebComer Jan 12 '22 at 09:58
  • And don't delete my comment again. It clearly states the problem with your solution. – WebComer Jan 12 '22 at 10:00