Java: Extending inner classes - java

I am trying to understand extending inner classes in Java. I have read around but nothing I found quite answers my question. So here goes...
I have...
public class Pie{
protected Slice[] slices;
// Pie constructor
public Pie(int n){
sliceGenerator(n)
}
private void sliceGenerator(int n){
slices = new Slice[n];
final float sweepAngle = 360.0f/(float)n;
float startAngle = 0;
for (int i=0;i<n;i++){
slices[i] = new Slice(startAngle);
startAngle += sweepAngle;
}
}
#Override
public String toString(){
for (Slice s:slices){
s.toString();
}
}
// Inner class...
public class Slice{
public Slice(float startAngle){
//set some private fields based on startAngle and generic pie
}
#Override
public String toString(){
return **string based on private fields**
}
}
}
Then I extend this...
public class ApplePie extends Pie{
protected Slice[] slices;
// Apple Pie constructor
public ApplePie(int n){
super(n);
}
// Inner class...
public class Slice extends Pie.Slice{
public Slice(float startAngle){
super(startAngle);
//set some **additional** private fields based on startAngle **specific to apple pie** appleness or something
}
#Override
public String toString(){
return **string based on apple pie specific private fields**
}
}
}
Now, when I make an Apple pie and call its toString method, like so...
ApplePie ap = new ApplePie(8);
System.out.println(ap.toString());
I do not get information about the apple pie slices, but information about the pie slices. It ignores my toString override, or more likely ignores my apple pie Slice. How can I arrange it such that apple pie slices refer to ApplePie?
Any help much appreciated! Sorry for pie references - it is the actual class I am working with...

I've changed your code to meet your requirements.
Your super class Pie is about to create a new instance of Slice, but the child class ApplePie's Slice does not override the Slice method of its super class'.
I added the functions below to enable the child class to create its own Slice.
protected void newSliceArray(int n) {
slices = new Slice[n];
}
protected Slice newSlice(float startAngle) {
return new Slice(startAngle);
}
Pie.java:
public class Pie {
private int a = 1;
protected Slice[] slices;
// Pie constructor
public Pie(int n) {
sliceGenerator(n);
}
private void sliceGenerator(int n) {
newSliceArray(n);
final float sweepAngle = 360.0f / n;
float startAngle = 0;
for (int i = 0; i < n; i++) {
slices[i] = newSlice(startAngle);
startAngle += sweepAngle;
}
}
protected void newSliceArray(int n) {
slices = new Slice[n];
}
protected Slice newSlice(float startAngle) {
return new Slice(startAngle);
}
#Override
public String toString() {
String t = "";
for (Slice s : slices) {
t += s.toString();
}
return t;
}
// Inner class...
public class Slice {
public Slice(float startAngle) {
// set some private fields based on startAngle and generic pie
}
#Override
public String toString() {
return "" + a;
}
}
}
ApplePie.java:
public class ApplePie extends Pie {
private int b = 2;
// protected Slice[] slices;
// Apple Pie constructor
public ApplePie(int n) {
super(n);
}
protected void newSliceArray(int n) {
slices = new Slice[n];
}
protected Slice newSlice(float startAngle) {
return new Slice(startAngle);
}
// Inner class...
public class Slice extends Pie.Slice {
public Slice(float startAngle) {
super(startAngle);
// set some **additional** private fields based on startAngle **specific to apple pie**
// appleness or something
}
#Override
public String toString() {
return b + "";
}
}
}
Test:
public static void main(String[] args) {
ApplePie ap = new ApplePie(8);
System.out.println(ap.toString());
}
The code will print 22222222

In your superclass, you are creating and storing Pie.Slice objects:
private void sliceGenerator(int n){
slices = new Slice[n];
final float sweepAngle = 360.0f/(float)n;
float startAngle = 0;
for (int i=0;i<n;i++){
slices[i] = new Slice(startAngle);
startAngle += sweepAngle;
}
}
These are the same objects being used by Pie.toString (which ApplePie doesn't override by the way).
Extending Pie with ApplePie and extending Pie.Slice with ApplePie.Slice doesn't change this. The new Slice(startAngle) in the above code does not magically switch to instantiating something different.
Aside from that, your Pie.toString() isn't returning anything - it shouldn't even compile:
#Override
public String toString(){
for (Slice s:slices){
s.toString();
}
}
I'm guessing you want to return a String representing all the slices. This would be a quick solution for example:
#Override
public String toString() {
return Arrays.toString(slices);
}
(Arrays.toString is just a utility method to get a String representing of an array.)

The answer lies within your program. When you instantiate Slice class, it gives call to the super class and invokes sliceGenerator. This method internally creates instances of Pie.Slice and not ApplePie.Slice. To get around this, make sliceGenerator method protected and override it in Apple.Slice class. Create the instances of Apple.Slice and it should work.

Related

How to make a Constructor without parameters?

I have to write a program that has a constructor without parameters. I created another short program as an example to show what I do not understand.
So I have a class with the main-method:
public class Dog {
public static void main(String[] args) {
CharacteristicsOfTheDog Dog1 = new CharacteristicsOfTheDog(20, 40);
System.out.println(Dog1.toString());
}
}
Now implemented another class:
public class CharacteristicsOfTheDog {
int size = 0;
int kilogram = 0;
public CharacteristicsOfTheDog(/*int size, int kilogram*/) {
// this.size = size;
// this.kilogram = kilogram;
}
public double getSize() {
return size;
}
public double getKilogram() {
return kilogram;
}
public String toString() {
return "The Dog is " + getSize() + " cm and " + getKilogram() + " kg";
}
}
In the class "CharacteristicsOfTheDog" in "public CharacteristicsOfTheDog()" I removed the parameters by commenting them out. So the Problem is: if I remove the parameters the program does not work:/ but my task is to do this without the parameters (as far as I understood). Can someone help me please?
Keep your no-arg constructor and then add setters for your properties:
public class CharacteristicsOfTheDog {
int size = 0;
int kilogram = 0;
public CharacteristicsOfTheDog() {
}
public void setSize(int size){
this.size = size;
}
public void setKilogram(int kilogram){
this.kilogram = kilogram;
}
}
In your other class, call:
CharacteristicsOfTheDog dog1 = new CharacteristicsOfTheDog();
dog.setSize(20);
dog.setKilogram(40);
As a suggestion, the naming of your class as CharacteristicsOfTheDog is rather literal and stating the obvious. Properties and methods of a class are what describes the characteristics of a class in terms of it's properties and behavior. If you just name your class Dog, that would be perfect. No need to state the obvious.
Unless CharacteristicsOfTheDog is a subclass or you have a constructor with parameters, you don't need an empty constructor. Just omit it. The following works just fine.
If the parent class has a constructor with arguments, then the parent class will need an explicit empty constructor, but the following should still work.
CharacteristicsOfTheDog cotd = new CharacteristicsOfTheDog();
cotd.setKilogram(100);
}
class CharacteristicsOfTheDog {
int size = 0;
int kilogram = 0;
public void setSize(int size){
this.size = size;
}
public void setKilogram(int kilogram){
this.kilogram = kilogram;
}
}
Depending on your use case, you might want to make the Characteristics class an interface and implement it.

Create new Instance of Child Class

I have an array list of Enemies and each enemy kind extends enemy. Now I don't wan't every same enemy kind to share all their stats, but I base my enemy selection of another array list. So I think the way to go would be to get the object of the array list containing all the options and then changing them to new Instances of the same class. My question is, how would I do that? Or do you guys have a better approach?
For easy of understanding here's what I mean abstracted
class shop{
ArrayList<Enemy> allEnemies;
}
class generator{
ArrayList<Enemies> selectedToGenerate = based on some of allEnemies
for(Enemy x : selectedToGenerate){ // i know this wouldn't work
x = newInstanceOf(x.getNonenemyThereforeChildclassClass());
}
}
hope this explains what I mean. Appreciate your time!
You can add a Builder to your Enemy.
abstract class Enemy {
private int strength;
public Builder<Enemy> getBuilder();
public static class Builder<T extends Enemy> {
int str;
public Builder<T> copyValues(T enemy) {
str = enemey.strength;
return this;
}
public Builder<T> strength(int s) {
str = s;
return this;
}
protected void fillValues(T toFill) {
toFill.strength = str;
}
protected abstract T createInstance();
public T build() {
T result = createInstance();
fillValues(result);
return result;
}
}
}
This Builder can create instances of your Enemy and fill it with values. For subclasses, you can extend the Builder by allowing it to fill more values.
class EnemyA extends EnemyA {
private int speed;
public Builder<EnemyA> getBuilder() {
return new Builder();
}
class EnemyABuilder extends Builder<EnemyA> {
int speed;
public EnemyABuilder copyValues(EnemyA enemy) {
super.copyValues(enemy);
speed = enemy.speed;
}
public EnemyABuilder speed(int s) {
speed = s;
return this;
}
protected void fillValues(EnemyA toFill) {
super.fillValues(toFill);
toFill.speed = speed;
}
protected EnemyA createInstance() {
return new EnemyA();
}
}
}
Now, you can create copies of the enemies by using their builders:
for(Enemy x : selectedToGenerate){ // i know this wouldn't work
Builder<? extends Enemy> builder = x.getBuilder();
builer.copyValues(x);
Enemy copy = builder.build();
}
As an additional bonus, you can use the builder to quickly create different versions of the same enemy.
EnemyA.Builder base = new Builder().strength(10);
EnemyA withSpeed1 = base.speed(1).build();
EnemyA withSpeed2 = base.speed(2).build();
EnemyA withSpeed3 = base.speed(3).build();

How to add a general vertical axis to a chart using ValueProvider

This is how I add a line to my chart at the moment. This is the abstract class for an arbitrry funciton I want to display:
public abstract class ArbitraryFunction implements
ValueProvider<ArbitraryFunctionData, Double> {
private String field;
public abstract Double f(Double x);
/**
* Constructor
*/
public ArbitraryFunction(String field) {
this.field = field;
}
#Override
public Double getValue(ArbitraryFunctionData object) {
return object.get(field);
}
#Override
public void setValue(ArbitraryFunctionData object, Double value) {
object.put(field, value);
}
#Override
public String getPath() {
return field;
}
}
This is how the chart is created:
ArbitraryFunction f1 = new ArbitraryFunction("f1") {
#Override
public Double f(Double x) {
return Math.sin(x);
}
};
functionMap.put(f1.getPath(), f1);
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
ArbitraryFunctionData d = new ArbitraryFunctionData();
d.setName("" + x);
for (Map.Entry<String, ArbitraryFunction> entry : functionMap.entrySet()) {
ArbitraryFunction tmp = entry.getValue();
d.put(tmp.getPath(), tmp.f(x));
}
store.add(d);
}
chart.setStore(store);
verticalAxis.setPosition(Position.LEFT);
verticalAxis.addField(f1);
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
This works so far as intended. The graph shows my lines as it should do it and the vertical axis is correct too. But I have problems drawing the horizontal axis since I don't know what I need to give horizontalAxis.addField( ??? ). I've tried a few things, but nothing worked.
Does anyone know how I need to set up the horizontal axis?
What do you want the horizontal axis value to be? Is it another NumericAxis - does each data point have a x value that it should be drawn on? Each d in your loop has a String name and some value - perhaps you want a CategoryAxis<ArbitraryFunctionData, String> that just draws those name values?
Looks like I misunderstood earlier - your Function objects are just used in setup, not in changing how you plot data
I'm still not sure what you are after, but it sounds like you mostly want to plot some lines. Each data point (ArbitraryFunctionData?) seems to have Y values for each function being used, and a title, but no X values, so there is no way to plot each point as (X,Y) with two numeric axes, just as (name, Y) using a CategoryAxis and a NumericAxis. This would end up more or less like this sample: http://www.sencha.com/examples/#ExamplePlace:linechart - strings along the bottom, and numbers along the side.
Here's take one, build mostly off of the idea/structure you already have:
public class FunctionPlotter implements EntryPoint {
public static class ArbitraryFunctionData {
private double xValue;
private Map<String, Double> yValues = new HashMap<String, Double>();
public double get(String key) {
return yValues.get(key);
}
public void put(String key, double yValue) {
yValues.put(key, yValue);
}
public double getXValue() {
return xValue;
}
public void setxValue(double xValue) {
this.xValue = xValue;
}
}
public interface AFDProperties extends PropertyAccess<ArbitraryFunctionData> {
//xvalue is unique, key off of that
#Path("xValue")
ModelKeyProvider<ArbitraryFunctionData> key();
//automatic ValueProvider generation for the get/setXValue methods
ValueProvider<ArbitraryFunctionData, Double> xValue();
}
/**
* This is really doing two different jobs at once - wasn't quite was I was trying to suggest in
* that other question. See the second version of this for clarification...
*/
public static abstract class ArbitraryFunction implements ValueProvider<ArbitraryFunctionData, Double> {
private final String field;
public ArbitraryFunction(String field) {
this.field = field;
}
public abstract Double f(Double x);
#Override
public Double getValue(ArbitraryFunctionData object) {
return object.get(field);
}
#Override
public void setValue(ArbitraryFunctionData object, Double value) {
object.put(field, value);
}
#Override
public String getPath() {
return field;
}
}
#Override
public void onModuleLoad() {
Viewport vp = new Viewport();
Set<ArbitraryFunction> functions = new HashSet<ArbitraryFunction>();
ArbitraryFunction f1 = new ArbitraryFunction("f1") {
#Override
public Double f(Double x) {
return Math.sin(x);
}
};
functions.add(f1);
AFDProperties props = GWT.create(AFDProperties.class);
ListStore<ArbitraryFunctionData> store = new ListStore<ArbitraryFunctionData>(props.key());
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
// Create one data object, and set the X value, since that is the same for all Y values
ArbitraryFunctionData d = new ArbitraryFunctionData();
d.setxValue(x);
// For each function, set the corresponding Y value
for (ArbitraryFunction func : functions) {
d.put(func.getPath(), func.f(x));
}
store.add(d);
}
Chart<ArbitraryFunctionData> chart = new Chart<ArbitraryFunctionData>();
chart.setStore(store);
//Y-axis
NumericAxis<ArbitraryFunctionData> verticalAxis = new NumericAxis<ArbitraryFunctionData>();
verticalAxis.setPosition(Position.LEFT);
verticalAxis.addField(f1);//needs to know this field to properly set the range of values
//f2, f3, etc
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
// X-Axis, this time reading from the xValue, not the series of ValueProviders
NumericAxis<ArbitraryFunctionData> horizAxis = new NumericAxis<ArbitraryFunctionData>();
horizAxis.setPosition(Position.BOTTOM);
horizAxis.addField(props.xValue());//same value for all
horizAxis.setSteps(2);
horizAxis.setMinorTickSteps(5);
chart.addAxis(horizAxis);
for (ArbitraryFunction func : functions) {
LineSeries<ArbitraryFunctionData> line = new LineSeries<ArbitraryFunctionData>();
// configure x axis
line.setXAxisPosition(Position.BOTTOM);//where is it
line.setXField(props.xValue());//what value do i use
// configure y axis
line.setYAxisPosition(Position.LEFT);//where is it
line.setYField(func);//what value do i use
//probably want to customized per func
line.setStroke(RGB.GRAY);
line.setStrokeWidth(2);
chart.addSeries(line);
}
vp.setWidget(chart);
RootPanel.get().add(vp);
}
}
And here's take two, this time with much simpler data and actually making the Function its own ValueProvider, and keeping the data dirt simple - just a double! Note that the ValueProvider is the function, and we never call getValue ourselves, we let the axis/series do it for us! Added a second function here to demonstrate that it does actually work.
public class FunctionPlotter implements EntryPoint {
/**
* Where did everything go? We're just making a ValueProvider now that can handle
* each number as a value, and working out the details from there
*
* For fun, added per-function coloring too
*/
public abstract static class Function implements ValueProvider<Double, Double> {
private final String name;
private final Color color;
public Function(String name, Color color) {
this.name = name;
this.color = color;
}
#Override
public abstract Double getValue(Double object);
#Override
public String getPath() {
return name;
}
#Override
public void setValue(Double object, Double value) {
//no-op
}
public Color getColor() {
return color;
}
}
#Override
public void onModuleLoad() {
Viewport vp = new Viewport();
Set<Function> functions = new HashSet<Function>();
Function f1 = new Function("f1", RGB.RED) {
#Override
public Double getValue(Double x) {
return Math.sin(x);
}
};
functions.add(f1);
Function f2 = new Function("f2", RGB.BLACK) {
#Override
public Double getValue(Double x) {
return Math.cos(x);
}
};
functions.add(f2);
//Turns out Stores can hold any objects - should probably factor out this key provider for reuse...
ListStore<Double> store = new ListStore<Double>(new ModelKeyProvider<Double>() {
#Override
public String getKey(Double item) {
return item.toString();
}
});
// collects the data of the functions and adds them to the store
for (Double x = 0.0; x <= 2 * Math.PI; x = x + 0.1) {
store.add(x);
}
Chart<Double> chart = new Chart<Double>();
chart.setStore(store);
//Y-axis
NumericAxis<Double> verticalAxis = new NumericAxis<Double>();
verticalAxis.setPosition(Position.LEFT);
for (Function func : functions) {
verticalAxis.addField(func);//needs to know this field to properly set the range of values
}
verticalAxis.setSteps(2);
verticalAxis.setMinorTickSteps(5);
chart.addAxis(verticalAxis);
// X-Axis, this time reading from the xValue, not the series of ValueProviders
NumericAxis<Double> horizAxis = new NumericAxis<Double>();
horizAxis.setPosition(Position.BOTTOM);
horizAxis.addField(new IdentityValueProvider<Double>());//magic value provider that returns the same string
horizAxis.setSteps(2);
horizAxis.setMinorTickSteps(5);
chart.addAxis(horizAxis);
for (Function func : functions) {
LineSeries<Double> line = new LineSeries<Double>();
// configure x axis
line.setXAxisPosition(Position.BOTTOM);//where is it
line.setXField(new IdentityValueProvider<Double>());//what value do i use
// configure y axis
line.setYAxisPosition(Position.LEFT);//where is it
line.setYField(func);//what value do i use
//probably want to customized per func
line.setStroke(func.getColor());
line.setStrokeWidth(2);
chart.addSeries(line);
}
vp.setWidget(chart);
RootPanel.get().add(vp);
}
}

How to write a method that returns an instance of an abstract class?

I am a beginner in Java and i trying to understand the abstract classes.
Below is the code that I've written; the question is: how do i write a method that will return an instance of that class.
public abstract class VehicleEngine
{
protected String name;
protected double fabricationCons;
protected double consum;
protected int mileage;
public VehicleEngine(String n, double fC)
{
name = n;
fabricationCons = fC;
mileage = 0;
consum = 0;
}
private void setFabricationCons(double fC)
{
fabricationCons = fC;
}
public abstract double currentConsum();
public String toString()
{
return name + " : " + fabricationCons + " : " + currentConsum();
}
public void addMileage(int km)
{
mileage += km;
}
public double getFabricationConsum()
{
return fabricationCons;
}
public String getName()
{
return name;
}
public int getMileage()
{
return mileage;
}
//public VehicleEngine get(String name){
//if(getName().equals(name)){
//return VehicleEngine;
//}
//return null;
//}
}
public class BenzinVehicle extends VehicleEngine
{
public BenzinVehicle(String n, double fC)
{
super(n, fC);
}
#Override
public double currentConsum()
{
if (getMileage() >= 75000) {
consum = getFabricationConsum() + 0.4;
} else {
consum = getFabricationConsum();
}
return consum;
}
}
public class DieselVehicle extends VehicleEngine
{
public DieselVehicle(String n, double fC)
{
super(n, fC);
}
#Override
public double currentConsum()
{
int cons = 0;
if (getMileage() < 5000) {
consum = getFabricationConsum();
} else {
consum = getFabricationConsum() + (getFabricationConsum() * (0.01 * (getMileage() / 5000)));
}
return consum;
}
}
This is the main.
public class Subject2
{
public static void main(String[] args)
{
VehicleEngine c1 = new BenzinVehicle("Ford Focus 1.9", 5.0);
DieselVehicle c2 = new DieselVehicle("Toyota Yaris 1.4D", 4.0);
BenzinVehicle c3 = new BenzinVehicle("Citroen C3 1.6",5.2);
c1.addMileage(30000);
c1.addMileage(55700);
c2.addMileage(49500);
c3.addMileage(35400);
System.out.println(c1);
System.out.println(c2);
System.out.println(VehicleEngine.get("Citroen C3 1.6")); //this is the line with problems
System.out.println(VehicleEngine.get("Ford Focus "));
}
}
And the output should be:
Ford Focus 1.9 : 5.0 : 5.4
Toyota Yaris 1.4D : 4.0 : 4.36
Citroen C3 1.6 : 5.2 : 5.2
null
You can not return an instance of an abstract class, by definition. What you can do, is return an instance of one of the concrete (non-abstract) subclasses that extend it. For example, inside the VehicleEngine you can create a factory that returns instances given the type of the instance and the expected parameters, but those instances will necessarily have to be concrete subclasses of VehicleEngine
Have a look at the Factory Method pattern. Your concrete classes will implement an abstract method that returns a class instance.
Abstract classes do not keep a list of their instances. Actually no Java class does that. If you really want to do that, you could add a static map to VehicleEngine like this:
private static Map<String, VehicleEngine> instanceMap = new HashMap<String, VehicleEngine>();
and change your get method to a static one like this:
public static VehicleEngine get(String name) {
return instanceMap.get(name);
}
and add this line to the end of the constructor of VehicleEngine:
VehicleEngine.instanceMap.put(n, this);
this way every new instance created puts itself into the static map. However this actually is not a good way to implement such a functionality. You could try to use a factory to create instances, or you could consider converting this class into an enum if you will have a limited predefined number of instances.

Getting a Java field through reflection, but not from its String name

Is it possible to get a Field through Java reflection if I have the field itself? It's a primitive float (public, no problem). I don't want to use its name as a String.
Example:
public class TVset {
public float voltageA;
public float voltageB;
public float voltageC;
public TVset(...) {...} // constructor
public void function() {...} // it changes voltages
}
class Voltmeter{
Object theObject;
Field theField;
Voltmeter(Object obj) {
theObject = obj;
Class theFieldClass = obj.getClass();
Class theContainerClass = theFieldClass.getDeclaringClass();
Field theField = ??? // <-- here I don't want to use a String
}
float getVoltage() {
return theField.getFloat(theObject);
}
}
TVset tv1 = new TVset(...);
TVset tv2 = new TVset(...);
Voltmeter meter = new Voltmeter(tv1.voltageB);
meter.getVoltage();
tv1.function();
meter.getVoltage(); <- should reflect the changed voltage
tv1.function();
meter.getVoltage(); <- should reflect the changed voltage
...
The effect is similar to passing the float by reference, but without wrapping it into a wrapper class.
I need to measure different voltages on different TV sets, just by changing the line:
Voltmeter meter = new Voltmeter(tv1.voltageB);
to something else, like:
Voltmeter meter = new Voltmeter(tv2.voltageA);
Is it possible to do it with reflection?
Thx
To use reflection you have to use a String. Instead of using a float you can use an object to wrap mutable float or a simple float[1];
BTW I wouldn't use float unless you have a really good reason, double suffers far less rounding error.
public class TVset {
public double[] voltageA = { 0.0 };
public double[] voltageB = { 0.0 };
public double[] voltageC = { 0.0 };
}
class Voltmeter{
final double[] theField;
Voltmeter(double[] theField) {
this.theField = theField;
}
double getVoltage() {
return theField[0];
}
}
// works just fine.
Voltmeter meter = new Voltmeter(tv1.voltageB);
EDIT: Using an abstract accessor. This is the fastest way to do this. AFAIK,the difference is less than 10 nano-seconds.
public abstract class Voltmeter{ // or use an interface
public abstract double get();
public abstract void set(double voltage);
}
public class TVset {
private double _voltageA = 0.0;
private double _voltageB = 0.0;
private double _voltageC = 0.0;
public final Voltmeter voltageA = new Voltmeter() {
public double get() { return _voltageA; }
public void set(double voltage) { _voltageA = voltage; }
}
public final Voltmeter voltageB = new Voltmeter() {
public double get() { return _voltageB; }
public void set(double voltage) { _voltageB = voltage; }
}
public final Voltmeter voltageC = new Voltmeter() {
public double get() { return _voltageC; }
public void set(double voltage) { _voltageC = voltage; }
}
}
Personally, if speed is critical, I would just use the fields directly by name. You won't get simpler or faster than that.
Just for completeness I've included the delegate way of solving this. I would also not recommend having your floats with public access.
public class stackoverflow_5383947 {
public static class Tvset {
public float voltageA;
public float voltageB;
public float voltageC;
public Tvset() {
}
public void function() {
voltageA++;
}
};
public static class Voltmeter {
private VoltageDelegate _delegate;
public Voltmeter(VoltageDelegate delegate) {
_delegate = delegate;
}
float getVoltage() {
return _delegate.getVoltage();
}
};
public static interface VoltageDelegate {
public float getVoltage();
}
public static void main(String[] args) {
final Tvset tv1 = new Tvset();
Voltmeter meter = new Voltmeter(new VoltageDelegate() {
public float getVoltage() {
return tv1.voltageA;
}
});
System.out.println(meter.getVoltage());
tv1.function();
System.out.println(meter.getVoltage());
tv1.function();
System.out.println(meter.getVoltage());
}
}
If you control the TVSet but need to use reflection for some reason, a good way to avoid errors is to write the method/field names that you need as String Constants in the TVSet class.
However if your concern is performance, reflection is not the way to go because accessing a field or method through reflection can be much slower than accessing through getters or directly.
Here a variant where you can give your float value instead of a string.
class Voltmeter{
Object container;
Field theField;
Voltmeter(Object obj, float currentValue) {
container = obj;
Class<?> containerClass = obj.getClass();
Field[] fields = containerClass.getFields();
for(Field f : fields) {
if (f.getType() == float.class &&
f.getFloat(container) == currentValue) {
this.theField = f;
break;
}
}
}
float getVoltage() {
return theField.getFloat(container);
}
}
Then call it like this:
Voltmeter meter = new Voltmeter(tv1, tv1.voltageB);
It works only if the voltages in the moment of Voltmeter creation are different (and not NaN), as it takes the first Field with the right value. And it is not really more efficient, I think.
I wouldn't really recommend this.

Categories