I am attempting to reference a variable using a certain string but have no idea how to do it. I know that I can use if statements if I really had to but I am sure that there is a simple way. An example is a Integer named dog. I would try to access the Integer using another string that contained the text dog.
private int dog;
String anything = "dog";
Is there anyway this is possible? Thanks!
Try this:
// use a map for referring to a value given its name
Map<String, Integer> vars = new HashMap<String, Integer>();
// for example, let's use these values
String anything = "dog";
int dog = 10;
// bind a value to a name
vars.put(anything, dog);
// retrieve the value, given its name
vars.get(anything);
=> 10
http://docs.oracle.com/javase/7/docs/api/java/util/HashMap.html
public static void main(String[] args) {
Map<String, MyObject> mapping = new HashMap<>();
}
Or new HashMap<String, MyObject>(); for pre java 7
You should use a Map of String to Integer. For example,
public static void main(String[] args) {
java.util.Map<String, Integer> dogMap = new java.util.HashMap<String, Integer>();
dogMap.put("Snoop", 10);
dogMap.put("doggy", 15);
dogMap.put("dog", 20);
System.out.println(dogMap);
}
Which outputs
{doggy=15, Snoop=10, dog=20}
Two options: create a Map<String, Object> that connects the two, or use reflection. I prefer reflection.
in order to get the field:
public class Test {
private int dog = 10;
private String anything = "dog";
public static void main(String[] args){
Test obj = new Test();
Object field = obj.getClass()
.getDeclaredField(obj.anything)
.get(obj);
System.out.println(field);
}
}
Output:
10
Create an object of the class that you will use. Then use the getDeclaredField() method on the class of that object. This will look into the private fields that are set, getField() holds only the public fields. That's it.
I've removed the try-catch from the post because it just clutters it.
Related
Imagine a situation where you have something like this :
class AbstractClass{void sharedMethod()}
class ClassOne extends AbstractClass{void sharedMethod(); void specificMethod()}
class ClassTwo extends AbstractClass{void sharedMethod(); void specificMethod2()}
I want to store in a Map something like this :
HashMap<String, List<SOMETHING_I_DON'T_KNOW_IN_ADVANCE>> hm = new HashMap<>();
hm.put("blabla", List<ClassOne>);
hm.put("blublu", List<ClassTwo>);
I know that I can put AbstractClass as the value type for my map but I want to have access to my specific methods. I already did some research to try to dynamic cast my object when I get them, but I didn't find anything satisfying.
Thanks in advance
Would creating "abstract void specificMethod()" in your AbstractClass, and then making concrete versions of specificMethod() in your concrete classes work for you?
That way you don't need to worry about whether to call specificMethodOne() or specificMethodTwo() ... the AbstractClass object already knows which version of specificMethod() it's supposed to use.
I know that I can put AbstractClass as the value type for my map but I want to have access to my specific methods.
I am assuming that you want to do this:
List<ClassOne> one = hm.get("blabla");
one.specificMethod();
// or
List<ClassTwo> two = hm.get("blublu");
two.specificMethod2();
Unfortunately you can't do this automatically because of type erasure. The Map and Lists have no idea what types they are storing so there is no way for them to help here. To them you are storing Objects.
Storing different types of objects in a Collection is a difficult pattern to support correctly and requires you to do your own casting once you get the values out of the Map.
List<AbstractClass> list = hm.get("blabla");
if (list instanceof ClassOne) {
ClassOne one = (ClassOne)list;
one.specificMethod();
} else if (list instanceof ClassTwo) {
ClassTwo two = (ClassTwo)list;
two.specificMethod2();
}
...
Yes this is gross and error prone but there is no easy way to do this using Java generics.
I would consider either encapsulating the data in a class, such as
private static class MyMessages
{
final List<ClassOne> typeOneMessages = new ArrayList<>();
final List<ClassTwo> typeTwoMessages = new ArrayList<>();
public void addTypeOne(ClassOne c) {
typeOneMessages.add(e);
}
// add appropriate getter methods; or a stream
public Stream<ClassOne> streamOne()
{
return typeOneMessages.stream();
}
// same for type two
}
Or follow what Rick Stabile suggested, and have a single method defined in the abstract class.
If you wish to keep them together, you will have to cast on retrieval, such as:
public static void main(String[] args)
{
final Map<String, List<? extends AbstractClass>> map = new HashMap<>();
List<ClassOne> ones = new ArrayList<>();
for (int i = 0; i < 4; i++) {
ones.add(new ClassOne());
}
List<ClassTwo> twos = new ArrayList<>();
for (int i = 0; i < 8; i++) {
twos.add(new ClassTwo());
}
map.put("ones", ones);
map.put("twos", twos);
List<? extends AbstractClass> out = map.get("ones");
// can call shared method on the contents
out.stream().forEach(a -> a.sharedMethod());
// will have to cast to the specific type
out.stream().filter(a -> a instanceof ClassOne)
.forEach(c1 -> ((ClassOne)c1).specificMethod());
List<? extends AbstractClass> out2 = map.get("twos");
// can call shared method on the contents
out2.stream().forEach(a -> a.sharedMethod());
// will have to cast to the specific type 2
out2.stream().filter(a -> a instanceof ClassTwo)
.forEach(c2 -> ((ClassTwo)c2).specificMethod2());
}
You can achieve that by using "wildcard(?) arguments" and because of the "Type Safety" mechanism a cast is inevitable.
This works for me:
HashMap<String, List<? extends AbstractClass>> hm = new HashMap<>();
ClassOne class1 = new ClassOne();
ClassTwo class2 = new ClassTwo();
List<ClassOne> c1l = new ArrayList<>();
c1l.add(class1);
List<ClassTwo> c2l = new ArrayList<>();
c2l.add(class2);
hm.put("blabla", c1l);
hm.put("blublu", c2l);
#SuppressWarnings("unchecked")
List<ClassOne> xyy = (List<ClassOne>)hm.get("blabla");
ClassOne c1 = xyy.get(0);
c1.specificMethod1();
and my output was:
Hello! from SpecificMethod1
I have two java class with constants, For Ex:
public class FirstClass {
public static final String STRING_A = "STRING_A";
public static final String STRING_B = "STRING_B";
public static final String STRING_C = "STRING_C";
...
}
public class SecondClass {
public static final String STRING_AA = "STRING_AA";
public static final String STRING_BA = "STRING_BA";
public static final String STRING_CA = "STRING_CA";
...
}
Now, I want to load these constants into a,
Map< String,String> classPropertyMap = new HashMap<>(); in Such a way that, Key for this map must be a constant from FirstClass and corresponding value must be a constant from SecondClass.
If it was just one class I could use reflection to load the fields, now since the constants are from two class, how could this be done?
Finally after loading the map, the contents of the map must be something like this:
First element : key and value is < STRING_A, STRING_AA>
Second element : key and value is < STRING_B, STRING_BA>
Third element : key and value is < STRING_C, STRING_CA>
I think if you really need it, the most robust way is to put the properties to a map explicitly.
I mean map.put(FirstClass.STRING_A, SecondClass.STRING_AA); and so on.
If you use reflection you rely on the properties and their declaration order never changes. If some new property is introduced in the library, it can break your code.
Try something like below, seems you can achieve.
FirstClass first = new FirstClass();
Field[] fields = first.getClass().getFields();
SecondClass second = new SecondClass();
Field[] fields1 = second.getClass().getFields();
Try This:
FirstClass first = new FirstClass();
String[] firstStrings = first.getStrings();
//Makes a String array, and fills it with the strings from the first class.
SecondClass second = new SecondClass();
String[] secondStrings = second.getStrings();
//Makes a String array, and fills it with the strings from the second class.
HashMap<String, String> classPropertyMap = new HashMap<String, String>();
int i = 0;
//Now put strings with the same index number from both arrays into the
//HashMap
while(i <= firstStrings.length()){
classPropertyMap.put(firstStrings.get(i), secondStrings.get(i);
i++;
}
Hope that works!
So I have a class called Test:
public class Test{
protected String name = "boy";
protected String mainAttack = "one";
protected String secAttack = "two";
protected String mainType"three";
protected String typeSpeak = "no spoken word in super class";
//Somehow put all the class variables in an Array of some sort
String[] allStrings = ??(all class' strings);
//(and if you feel challenged, put in ArrayList without type declared.
//So I could put in, not only Strings, but also ints etc.)
public void Tester(){
//Somehow loop through array(list) and print values (for-loop?)
}
}
As you can see, I want to put all the class variables in an Array or ArrayList (or something similar) automatically.
And next I want to be able to loop through the array and print/get the values.
Preferably using an enhanced-for loop.
As other said, don't do this. But this is how:
Class<?> cl = this.getClass();
List<Object> allObjects = new ArrayList<Object>();
for (java.lang.reflect.Field f: cl.getDeclaredFields())
{
f.setAccessible(true);
try
{
Object o = f.get(this);
allObjects.add(o);
}
catch (Exception e)
{
...
}
}
for (Object o: allObjects)
System.out.println(o);
If you really really need do this you need to use Reflection.
However a much better approach would be to store the values in a Map (probably a HashMap) and then you can query/set/etc them from that easily.
You can use Map or Hashmap to store variables and its values instead of Array or Arraylist
HashMap is an object that stores both “key/value” as a pairs. In this article, we show you how to create a HashMap instance and iterates the HashMap data.
Why not use a HashMap for the values and iterate through that?
Iterate through a HashMap
Do this.
String threeEleves = "sky";
String sevenDwarves = "stone";
String nineMortal = "die";
String oneRing[] = new String[] // <<< This
{
threeElves,
sevenDwarves,
nineMortal
}
or do this
// in some class.
public void process(final String... varArgs)
{
for (String current : varArgs)
{
}
}
String one = "noodles";
String two = "get";
String three = "in";
String four = "my";
String five = "belly";
process (one, two, three, four, five);
Example code:
int width = 5;
int area = 8;
int potato = 2;
int stackOverflow = -4;
Now, say I want to have the user input a string:
String input = new Scanner(System.in).nextLine();
Then, say the user inputs potato. How would I retrieve the variable named potato and do stuff with it? Something like this:
System.getVariable(input); //which will be 2
System.getVariable("stackOverflow"); //should be -4
I looked up some things and did not find much; I did find a reference to something called "the Reflection API," but that seems too complicated for this one simple task.
Is there a way to do this, and if so, what is it? If "Reflection" does indeed work and if it is the only way, then how would I use it to do this? The tutorial page for it has all sorts of internal stuff that I can't make any sense of.
EDIT: I need to keep the Strings in the variables for what I am doing. (I can't use a Map)
Using reflection doesn't seem like a good design for what you're doing here. It would be better to use a Map<String, Integer> for example:
static final Map<String, Integer> VALUES_BY_NAME;
static {
final Map<String, Integer> valuesByName = new HashMap<>();
valuesByName.put("width", 5);
valuesByName.put("potato", 2);
VALUES_BY_NAME = Collections.unmodifiableMap(valuesByName);
}
Or with Guava:
static final ImmutableMap<String, Integer> VALUES_BY_NAME = ImmutableMap.of(
"width", 5,
"potato", 2
);
Or with an enum:
enum NameValuePair {
WIDTH("width", 5),
POTATO("potato", 2);
private final String name;
private final int value;
private NameValuePair(final String name, final int value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
static NameValuePair getByName(final String name) {
for (final NameValuePair nvp : values()) {
if (nvp.getName().equals(name)) {
return nvp;
}
}
throw new IllegalArgumentException("Invalid name: " + name);
}
}
Variable names are only available at compiler time. Reflection only gives access to class declarations and items declared inside them, but not to local variables. I suspect that a Map of some kind will be a more appropriate solution to your real problem. Specifically, check out HashMap and TreeMap.
Instead of trying to find the value of a variable name, why don't you use a Map with a key/value pair?
Map<String, Integer> vars = new HashMap<String, Integer>();
vars.put("width",5);
vars.put("area",8);
vars.put("potato", 2);
vars.put("stackOverflow",-4);
Then you could access the inputs like so:
vars.get(input); //would be 2
vars.get("stackOverflow"); //would be -4
I have another solution without a map :
class Vars {
Integer potato, stack;
public Vars(a,b) {
potato=a;
stack=b;
}
}
Object object=(Object)new Vars(1,2);
Class<?> c = object.getClass();
Integer result=(Integer)c.getField("potato").get(object);
I have a solution for this problem that does not involve using a map. I ran into this technique because we had several variables that needed to be update based on something within the variable name itself. However, the best way to do this is by using the getters/setters rather than the variables.
After you create your class, you can access the methods by creating Method objects and invoking them individually.
public class FooClass
private String foo1;
private String foo2;
public String getFoo1();
public String getFoo2();
FooClass fooClass = new FooClass();
Method mFoo1 = fooClass.getClass().getMethod("getFoo" + increment + "()");
mFoo1 .invoke(fooClass);
However, this would not be limited to only incremental numbers, as long as you can get the string to match the method exactly.
String value = "Potato";
Method mPotato = myClass.getClass().getMethod("get" + value+ "()");
mPotato.invoke(myClass);
Very redundant, but you can keep your variable names when using a map:
int width = 5;
int area = 8;
int potato = 2;
int stackOverflow = -4;
Map<String, Integer> map = new HashMap<>();
map.put("width", width);
map.put("area", area);
map.put("potato", potato);
map.put("stackOverflow", stackOverflow);
But a statement like this:
width = 42;
would not change the value in the Map:
String input = "width";
map.get(input); // <-- still returns 5.
Only a new call of put fixes that:
width = 42;
map.put("width", width);
// or
map.put("width", 42);
Is there a better way of creating arrays from elements of an enum:
public static enum LOGICAL {
AND ("&", "AND"),
OR ("||", "OR");
public final String symbol;
public final String label;
LOGICAL(String symbol, String label) {
this.symbol=symbol;
this.label=label;
}
}
public static final String[] LOGICAL_NAMES = new String[LOGICAL.values().length];
static{
for(int i=0; i<LOGICAL.values().length; i++)
LOGICAL_NAMES[i]=LOGICAL.values()[i].symbol;
}
public static final String[] LOGICAL_LABELS = new String[LOGICAL.values().length];
static{
for(int i=0; i<LOGICAL.values().length; i++)
LOGICAL_LABELS[i]=LOGICAL.values()[i].label;
}
Personally I wouldn't expose them as an array, whose contents can be changed by anyone. I'd probably use an unmodifiable list instead - and probably expose that via a property rather than as a field. The initialization would be something like this:
private static final List<String> labels;
private static final List<String> values;
static
{
List<String> mutableLabels = new ArrayList<String>();
List<String> mutableValues = new ArrayList<String>();
for (LOGICAL x : LOGICAL.values())
{
mutableLabels.add(x.label);
mutableValues.add(x.value);
}
labels = Collections.unmodifiableList(mutableLabels);
values = Collections.unmodifiableList(mutableValues);
}
(If you're already using Guava you might even want to use ImmutableList instead, and expose the collections that way to make it clear that they are immutable.)
No. That seems the proper way. Even if there was some utility, it would rely on reflection
If you are using it often cache it in the enum
If you use your values very frequently and your enumeration gets bigger use Maps. Declare the following in your class.
private static EnumMap<LOGICAL,String> symbols = new EnumMap<LOGICAL, String>(LOGICAL.class);
and then just below it:
static{
for(LOGICAL i : LOGICAL.values().)
symbols.put(i, i.symbol);
}
then you can use symbols.values() or symbols.get(LOGICAL.AND) etc.