简体   繁体   中英

Access another class's data member's field in Java

So, I want to access one class's data member's field from a whole other class through reflection. I have been unable to figure out, how, after I get the data member through reflection, I can change the field's value. I don't really know how to express it better, so I will let the code speak for me.

Here follows the handler class that calls the buttons. Following are the rest of the classes, whose functionality I will explain on the go.

import java.awt.*;
import java.awt.event.*;

public class SimHandler extends Frame{

    public myValveButton but0,but1,but2,but3,but4,but5,but6,but7;
    public SimHandler(){    
        super("Liquer Plant Control Panel");
        this.setLayout(null);
        this.setFont(new Font("Helvetica", Font.PLAIN, 14));
        this.setBackground(Color.black);

        but0 = new myValveButton("S1a",100,40,this);
        but1 = new myValveButton("S1b",100,140,this);
        but2 = new myValveButton("S2a",200,40,this);
        but3 = new myValveButton("S2b",200,140,this);
        but4 = new myValveButton("S3a",100,240,this);
        but5 = new myValveButton("S3b",100,340,this);
        but6 = new myValveButton("S4a",200,240,this);
        but7 = new myValveButton("S4b",200,340,this);

        this.setSize(335,410);
        this.setLocation(100,100);
        this.setVisible(true);
        this.toFront();            
        this.setResizable(false);  
        this.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

    }
}

This is actually where I try to use reflection to change the value of Silo's instance state. Below this one follow LiqPlantSim and Silo classes. As you can see, the state variable cannot be resolved that way, and after quite some googling, I can't figure out how I can make it work.

import java.awt.Button;
import java.awt.Frame;
import java.awt.event.*;
import java.lang.reflect.Field;

public class myValveButton extends Button{
    String label;
    public myValveButton(String label,int x,int y,Frame f){
        super(label);
        this.label = label;
        this.addActionListener(new myValveButtonHandler(label));
        f.add(this);
        this.setBounds(x, y, 35, 30);
        }
}

class myValveButtonHandler implements ActionListener{
    Field f;
    String label;
    public myValveButtonHandler(String label){
        this.label = label;
    }

    public void actionPerformed(ActionEvent pushButton){
        try {
            f = LiqPlantSim.class.getDeclaredField("silo"+label.split("")[1]);
            System.out.println(f);
            //f.state = "full"   //Eclipse says 'state cannot be resolved to a type or is not a field'
        } catch (NoSuchFieldException e) {
        } catch (SecurityException e) {
        }
        System.out.println(label.split("")[2]);
    }
}

Here is the LiqPlantSim class.

import java.util.HashMap;
import java.util.Map;

public class LiqPlantSim{   

    public Silo silo1,silo2,silo3,silo4;
    public Pipe pipe;

    public LiqPlantSim(){       
        silo1 = new Silo(false,false);
        silo2 = new Silo(false,true);
        silo3 = new Silo(true,false);
        silo4 = new Silo(true,true);
        pipe = new Pipe();          
    }
}

Here is the Silo Class.

public class Silo {

    public boolean mixer,resistance;
    public String state,mixerState,resState;
    public Silo(boolean mix,boolean res){
        mixer = mix;
        resistance = res;
        state = "empty";
    }

}

Apart from finding out how I can access the silo's state variables, I would really appreciate any feedback and/or advice on how I could structure my work better, and on any mistakes I might have made.

First off, Class#getDeclaredField(String) returns a Field object, not the actual value of that field. To get the value, you must use Field#get(Object) , where the argument is an instance of the class for which you are trying to access a field. In your code, this would be:

LiqPlantSim sim = doSomethingToGetInstance();
f = LiqPlantSim.class.getDeclaredField("siloX");
Silo silo = (Silo) f.get(sim);

Which brings me to my next point: Why use reflection at all? Your answer probably has to do with getting the correct Silo using the label. You should* restructure LiqPlantSim to use an array or a List to solve this:

import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.ArrayList;

public class LiqPlantSim{   

    private List<Silo> silos;
    public Pipe pipe;

    public LiqPlantSim(){
        silos = new ArrayList<>();
        silos.add(new Silo(false,false));
        silos.add(new Silo(false,true));
        silos.add(new Silo(true,false));
        silos.add(new Silo(true,true));
        pipe = new Pipe();          
    }

    public Silo getSilo(int index) {
        return silos.get(index);
    }

    //Possibly other methods to access silos
}

(EDIT: You should do the same with the buttons in SimHandler )

Then, in the handler, you can access a Silo like this:

 public void actionPerformed(ActionEvent pushButton){
    try {
        int labelLength = label.length();
        int index = Integer.parseInt(label.substring(labelLength - 1, labelLength));
        Silo silo = doSomethingToGetLiqPlantSimInstance().getSilo(index);
        silo.state = "full" //Note that it is good practice to use private fields and public getters and setters
    } catch (NoSuchFieldException e) {
    } catch (SecurityException e) {
    }
    System.out.println(label.split("")[2]);
}

Better yet, get the index in the constructor and store it so you don't have to recalculate it every time.

*Of course, this is only a suggestion

This: "silo"+label.split("")[1] will create the String siloS . To get the number from the label variable try: label.substring(1,2)

Also you don't need to pass the Frame f to the myValveButton constructor, you can add the buttons to the frame directly in the SimHandler using this.add(but0) .

Why do you implement your action-listeners in the button class? The ValveButton should not be aware of what to do when it is clicked.

Instead you should implement your action-listener in your SimHandler class. After instantiating your 8 ValveButtons you can add the action-listeners in a loop.

Anyways - if you really need to go for a solution using reflection I would recomment using the tiny PrivilegedAccessor framework. Though recommended to be used only in unit-tests it might be useful in your case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM