在 Java 中访问另一个 class 的数据成员字段

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

所以,我想通过反射从另一个 class 访问一个 class 的数据成员字段。我一直想不通,在通过反射获取数据成员后,如何更改字段的值。实在不知道怎么表达比较好,就让代码来说话吧。

下面是调用按钮的处理程序 class。以下是 classes 的其余部分,我将在旅途中解释其功能。

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);
            }
        });

    }
}

这实际上是我尝试使用反射来更改 Silo 实例状态值的地方。下面是 LiqPlantSim 和 Silo classes。如您所见,状态变量无法以这种方式解析,经过相当多的谷歌搜索后,我无法弄清楚如何让它工作。

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]);
    }
}

这是 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();          
    }
}

这是筒仓 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";
    }

}

除了了解如何访问 silo 的状态变量之外,我非常感谢任何反馈 and/or 关于如何更好地组织我的工作以及我可能犯的任何错误的建议。

这:"silo"+label.split("")[1] 将创建 String siloS。 要从 label 变量中获取数字,请尝试:label.substring(1,2)

此外,您不需要将 Frame f 传递给 myValveButton 构造函数,您可以使用 this.add(but0) 直接在 SimHandler 中将按钮添加到框架中.

为什么要在按钮中实现动作侦听器 class?单击 ValveButton 时不应知道要做什么。

相反,您应该在您的 SimHandler class 中实现您的动作侦听器。实例化 8 个 ValveButton 后,您可以在循环中添加动作侦听器。

无论如何 - 如果您真的需要使用反射来寻求解决方案,我会推荐使用微型 PrivilegedAccessor 框架。虽然建议仅在单元测试中使用,但它可能对您的情况有用。

首先,Class#getDeclaredField(String) returns 一个 Field 对象,而不是该字段的实际值。要获取该值,您必须使用 Field#get(Object),其中参数是您尝试访问其字段的 class 的实例。在您的代码中,这将是:

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

这引出了我的下一点:为什么要使用反射?您的答案可能与使用标签获得正确的 Silo 有关。您应该*重组 LiqPlantSim 以使用数组或 List 来解决此问题:

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
}

(编辑:您应该对 SimHandler 中的按钮执行相同的操作)

然后,在处理程序中,您可以像这样访问 Silo

 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]);
}

更好的是,在构造函数中获取索引并将其存储,这样您就不必每次都重新计算它。

*当然,这只是一个建议