-1

I am currently working on a very simple client-server-connection. The client and the server are connected through sockets and communicate by using streams.

Now I want the client to be able to invoke methods on the server. Therefore the client should send a message to the server. This message is then split into two Strings. The first string methodName contains the name of the method to be called. The second String objectName contains the name of an object on which the method should be called.

One way to this would be by using if-statements like if(methodName.equals("mymethod")){mymethod()};. This method works just fine and can be extended to check for objectName as well. Still I am very unhappy with this solution. I want to be able to invoke a lot of different methods on a lot of different objects (even on objects that don't even exist yet). I just don't want to write thousand of if-statements or cases.

That's why I try to find a different approach. I already found a partial solution for methodName by using this:

java.lang.reflect.Method method = myObject.getClass().getMethod(methodName);
method.invoke(myObject);

This would effectively turn the String value of methodName into a reference to the corresponding method. That's just what I was looking for.

But it comes with the problem that both instructions require an object reference. By now the "name" of that reference is still stored in objectName and most important, it is a String value and not a reference. What I am now looking for is a instruction like this:

Object myObject = turnTheStringValueIntoAReference(objectName);

For now we can just assume that there already exists an object reference with the name that is stored in objectName. Otherwise there might also be a way to do something like this:

Object turnTheStringValueIntoAReference(objectName) = new Object();

I tried my best to find a way to turn a String value into a object reference but sadly I couldn't find any solution. I now have reached to point where I was thinking that this "feature" purposely doesn't exist in Java because it would basically eliminate type safety. Not to mention all the exceptions that might occur.

However I still would be very happy and very thankful if anyone could tell me if there actually is a way to do this. Maybe I just didn't figure it out yet.

With further research I found out that RMI might be an alternative to this. I guess RMI would even be a much better way to this. The reason I still don't want to RMI is that I plan on expanding the server in a way that it doesn't only accept my own client but also clients that are written in different languages. I think managing this will be ways easier if the client only needs to send a message than somehow support Java RMI without being written in Java.

Well, I think that's it. I would really appreciate your help.

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Jan
  • 63
  • 4
  • Isn't Class.instanceOf() what you want? Class.forName() takes a String. All this has been part of the JDK since 1.0. A better question is: should you do this? Reflection might not be the best idea. Do your clients really need this kind of flexibility? – duffymo Jun 27 '23 at 17:23
  • Would something like this work? https://stackoverflow.com/questions/6094575/creating-an-instance-using-the-class-name-and-calling-constructor – Francis Godinho Jun 27 '23 at 17:27
  • @duffymo Thanks for your answer. I don't really understand how this would work. Class.forNanme returns a Class object. But having the class object doesn't allow me to call a method on an instance of this class. What I would need is something like Object.forName(). Am I missing something? – Jan Jun 27 '23 at 17:55
  • @FrancisGodinho Thanks for your answer. Maybe there is some way to make it work but I cannot see it yet. What's done here is creating a new object. That wouldn't be what I intended but I would be acceptable. But in this case I would have to have some object holding the information for the constructor. This again rises the question on how to address that object. I cannot rely on using primitiv data types only. – Jan Jun 27 '23 at 18:03
  • A search through SO would get you the answer. It's not a pretty one unless all the classes you want to invoke have similar constructor arguments: https://stackoverflow.com/questions/1782598/with-java-reflection-how-to-instantiate-a-new-object-then-call-a-method-on-it – duffymo Jun 27 '23 at 19:29
  • @duffymo I understand the threads that you linked. But I don't understand how they should be related to my problem. The second link tells me how to instantiate an Object a class. What I want to do is getting the reference of an object that already exists. – Jan Jun 27 '23 at 22:59
  • Then you should be able to pass the instance you have to Method.invoke. Keep the instances in a HashMap and look them up by name. I've written code and provided links. You should be able to figure it out from what I've posted. It's _four lines of code_ in the try block below. How hard is that to understand? – duffymo Jun 28 '23 at 00:02
  • I’m voting to close this question because it shows no effort on the part of the OP. – duffymo Jun 28 '23 at 00:03
  • @duffymo I am sorry man. I didn't even know of HashMap yet. I am a beginner. If you are really trying to help, why don't you just mention HashMap. It's a single word and I could have looked it up. – Jan Jun 28 '23 at 00:24
  • You are reimplementing RMI. Why? – user207421 Jun 28 '23 at 00:50
  • I did mention HashMap. My advice to a beginner: don't do this. Reflection is not a good idea. You are thinking about this problem all wrong. – duffymo Jun 28 '23 at 11:25

2 Answers2

0

I now have reached to point where I was thinking that this "feature" purposely doesn't exist in Java because it would basically eliminate type safety.

You are correct. There is no way to access variable names -- at least not local variable names -- in Java. You can access fields, at least, with getField(fieldName), but this is still a terrible idea.

You should really look into existing solutions that solve these problems in principled, cross-language ways. Any RPC or REST system would be a good start. This is not an area where you have to invent your own solution.

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • Thank you for your answer. using getField() is actually a interesting way that might solve my problem in a efficient way. – Jan Jun 27 '23 at 18:07
  • Of course you are right. My project is basically non-sense because there a already existing solutions. I am just trying to understand how things work in detail by building them from scratch. In this case I want to try to develop my own protocol. I thought that the communication between sockets by using streams is quit similar to what existing protocols (such as http) do. Maybe I have to look that up more carefully. – Jan Jun 27 '23 at 18:13
  • Most principled solutions to this sort of problem do not work the way your design works. Even if you want to reimplement a solution from scratch, you should look to other examples for their design and how they solve (or don't solve) problems like this. – Louis Wasserman Jun 27 '23 at 19:30
  • That makes sense, yes. I am not to deep into the topic yet and I have to do further research. But I really struggle to find information on how exactly things are done. E.g. I tried to find a complete but minimalistic implementation of a http server. And well, some days later I am still searching for something I can understand. There's always information that's too general (like server and client communicate using protocols) and then there's information that's way to specific to understand the bigger picture, nothing in between. It just feels like I'm missing the right keyword. – Jan Jun 27 '23 at 23:12
  • I have now looked at a few more examples and I think I begin to understand what's wrong with my attempt. Am I right, that using sockets is perfectly fine but the mistake I made was using (or trying to use) reflection? I found many examples of things like simple command based chat rooms and even web servers, that just use sockets. The thing that's different is that they use if-statements instead of reflection. For clarification, I would appreciate your answer so that I know if I am on the right way. I am also looking up RPC and REST as you suggested. – Jan Jul 01 '23 at 05:40
  • Using reflection is definitely a bad idea and almost certainly the wrong direction. – Louis Wasserman Jul 01 '23 at 22:20
0

Here's an example of what is possible for you:

Person:

package reflection;

public class Person {

    private final String firstName;
    private final String lastName;

    public Person() {
        this("Tom", "Jones");
    }

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }
}

Address:

package reflection;

public class Address {

    private final String street;
    private final String city;
    private final String state;
    private final String zip;

    public Address() {
        this("100 Fifth Avenue", "New York", "NY", "10003");
    }

    public Address(String street, String city, String state, String zip) {
        this.street = street;
        this.city = city;
        this.state = state;
        this.zip = zip;
    }

    public String getStreet() {
        return street;
    }

    public String getCity() {
        return city;
    }

    public String getState() {
        return state;
    }

    public String getZip() {
        return zip;
    }
}

ReflectionSample:

package reflection;

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

/**
 * @link https://stackoverflow.com/questions/76567190/how-to-turn-a-string-value-into-a-object-reference-in-java?noredirect=1#comment134999266_76567190
 * @link https://stackoverflow.com/questions/1782598/with-java-reflection-how-to-instantiate-a-new-object-then-call-a-method-on-it
 */
public class ReflectionSample {

    public static void main(String[] args) {
        if (args.length > 1) {
            String className = args[0];
            String methodName = args[1];
            try {
                Constructor<?> defaultConstructor = Class.forName(className).getConstructor();
                Object instance = defaultConstructor.newInstance();
                Method method = instance.getClass().getMethod(methodName);
                System.out.println(method.invoke(instance));
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | IllegalAccessException |
                     InvocationTargetException e) {
                System.err.println(className + " class with method name " + methodName + " not found");
            }
        } else {
            System.out.println("Usage: reflection.ReflectionSample <class> <method>");
        }
    }
}

This sample will instantiate instances of Person and Address and call the method I wish on that instance. I pass the class and method names in as command line arguments (e.g. "reflection.Person getFirstName" and "reflection.Address getState")

But you see the problems that are built into this approach. I would not recommend that you go this way. You overestimate the requirement of flexibility for your clients.

duffymo
  • 305,152
  • 44
  • 369
  • 561
  • I appreciate your answer. Still it's not really what I am look for. This way enforces the instantiation of a new object. That's not really what I want to do. I want to adress an object that already exists (and only in case that it does not exist create a new one). – Jan Jun 28 '23 at 00:33