I have a class declared to hold information. Let's say it has these fields
class data {
int a;
int b;
int c;
}
And I want to access these fields like this:
String [] fields = {"a", "b", "c"};
data da = new data();
for (int i = 0; i < 3; i++)
if (da.fields[i] < 10)
dosomething();
Is there any way in Java to do this? Googling, I got some results about something called "reflection, " but I've never really heard of that, and I don't think it's quite what I want. Is there any way to do this in Java? If not, are there any languages that support this kind of thing (just out of curiosity)?
Reflection is probably what you need. But what you need more is a hard look at your design. You should avoid reflection where possible.
If you are still interested in doing this, take a look at Java Reflection: Fields.
Field field = aClass.getField("someField");
Is what you would do to get the field by that name. A more detailed example of what you want.
Class aClass = MyObject.class
Field field = aClass.getField("someField");
MyObject objectInstance = new MyObject();
Object value = field.get(objectInstance);
field.set(objetInstance, value);
You could add a suitable API to your class:
class data {
int a;
int b;
int c;
int get(String field) {
if (field.equals("a")) return a;
if (field.equals("b")) return b;
if (field.equals("c")) return c;
throw new IllegalArgumentException();
}
}
Then you could:
String [] fields = {"a", "b", "c"};
data da = new data();
for (int i = 0; i < 3; i++)
if (da.get(fields[i]) < 10)
dosomething();
Reflection is the way to go. Karthik T has the correct answer.
Without using reflection:
Declare the fields to be an array (or a Map), and the names as lookups:
class data {
int[] fields = new int[3];
public static final int A = 0;
public static final int B = 1;
public static final int C = 2;
}
public void foo() {
data da = new data();
int[] fields = {data.A, data.B, data.C};
for (int n : fields) {
if (da.fields[n] < 10) doSomething();
}
}
Its ugly, and your design is probably wrong if you want to do something like this, but it works.
You don't really say what's your real problem; so it's hard to say if using reflection or something else is what you need. However, when someone talk about associating a string value with another variable, what they often need to use is a Map (or a dictionary in other languages); something along the line of:
TreeMap<String, Integer> tm = new TreeMap<String, Integer>();
tm.put("a", 1);
tm.put("b", 2);
tm.put("c", 3);
for (String k: tm.keySet()) {
System.out.println(k + ": " + tm.get(k));
}
Related
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);
I read an Excel table containing four columns and create a List. Now, I'd like to use the first three columns as key and use the last column as value. I've seen similar questions asked, but in all those questions, either String or Integer is used as a key.
public class initial {
private int from;
private int to;
private int via;
private int cost;
//constructor
//set and get methods
//hashCode and equals methods
}
public class myTuple {
private int from;
private int to;
private int via;
}
//main function
//get the Excel Table as a list
ArrayList<initial> myList = new ArrayList<initial>();
for(int i= mySheet.getFirstRowNum()+1 ; i<= mySheet.getLastRowNum(); i++) {
initial e = new initial();
Row ro = mySheet.getRow(i);
for(int j = ro.getFirstCellNum(); j <= ro.getLastCellNum(); j++) {
Cell ce = ro.getCell(j);
switch(j) {
case 0:
e.setFrom((int) ce.getNumericCellValue());
break;
.....
case 3:
e.setCost((int) ce.getNumericCellValue());
break;
}
}
myList.add(e);
}
//Create map
Map<myTuple, Integer> myMap = new HashMap<>();
I do not know how to proceed after this point. I believe I should use something like;
Map<myTuple, Integer> myMap= myList.stream().collectC(ollectors.toMap(myList:: , myList::));
If someone could assist me, I'd really appreciate.
Also, if you believe that there is a more efficient way to perform this (e.g., the way I read my data and parse into a list, the way I convert the list into a map), please let me know. Even though it is not in the content of this question, if there is a better way to read a multi dimensional table and parse into a List as I do, I 'd love to hear that too. In the future, I will have a bigger tables with more columns. Hence, I'm not quite sure if going through every column with a switch statement is the way to go.
You can just create the map while looping.
Tuple key = new Tuple(row.getNum(0), row.getNum(1), row.getNum(2));
List<Integer> value = new ArrayList<>();
for (int cell = 3; cell < row.getCount(); cell++) {
value.add(row.getNum(cell));
}
Map.put(key,value);
The toMap collector needs 2 functions (1 to create a key & 1 to create a value). You can use lambdas (to extract the relevant fields from your source type):
Map<MyTuple, Integer> myMap = myList
.stream()
.collect(Collectors.toMap(
i -> new myTuple(i.from, i.to, i.via),
i -> i.cost
));
Your destination type "MyTuple" needs a constructor, equals, and hashcode.
Here is an example:
class Tuple implements Comparable<Tuple> {
Object one;
Object two;
Object three;
public Tuple(final Object one, final Object two, final Object three) {
this.one = one;
this.two = two;
this.three = three;
}
#Override
public int compareTo(final Tuple that) {
// TODO: Do your comparison here for the fields one, two and three
return 0;
}
}
Map<Tuple, Object> mapKeyedByCompositeTuple = new HashMap<>();
// TODO: Inside your loop
for (int i = 10; i > 0; i--) {
Tuple key = new Tuple("cell-one-value-" + i, "cell-two-value-" + i, "cell-three-value-" + i);
mapKeyedByCompositeTuple.put(key, "cell-four-value-" + i);
}
System.out.println(mapKeyedByCompositeTuple);
Hope that helps,
Cheers,
Michael
I want to know if is it possible to Store a String variable on a String array?
I cant explain it well but here is what i do:
String st1 = "",st2 = "",st3 = "",st4 = "";
String[] str = {st1,st2,st3,st4};
Unfortunately when i use for loop the str gets the value of st1 and st2 and st3 ans st4 not the variable it self..
This is what i want to do exactly on my mind..
Whenever a have a String array for example:
String[] containsValue = { "hi", "hello", "there" };
String strHi, strHello, strThere;
String[] getContainsValue = { strHi, strHello, strThere };
for (int x = 0; x < getContainsValue.length; x++) {
getContainsValue[x] = containsValue[x];
}
The value of:
strHi = "hi"
strHello = "hello"
strThere = "there";
Basically i want to transfer that value of containsValue[] to 3 String which is strHi, strHello, strThere that are stored in getContainsValue[]. Then use for loop to asign value to them came from containsValue[].
Is this posible? If so then can you give me some format how to do it? thanks..
You can use Map<K,V>.
Map<String,String> map=new HashMap<String,String>();
map.put("strHi","hi");
map.put("strHello","hello");
map.put("strThere","there");
System.out.println(map.get("strHello"));
You can use enum class as the Array needed :
public enum EnumModifE {
str1("1"), str2("2"), str3("3");
String value;
EnumModifE(final String s) {
this.value = s;
}
public void setValue(final String s) {
this.value = s;
}
}
public class EnumModifM {
public static void main(final String[] args) {
for (final EnumModifE eme : EnumModifE.values()) {
System.out.println(eme + "\t" + eme.value);
}
EnumModifE.str1.setValue("Hello");
EnumModifE.str2.setValue("all");
EnumModifE.str3.setValue("[wo]men");
for (final EnumModifE eme : EnumModifE.values()) {
System.out.println(eme + "\t" + eme.value);
}
}
}
Output
str1 1
str2 2
str3 3
str1 Hello
str2 all
str3 [wo]men
See in Effective Java use of enum
The concept you are looking for is an "l-value". Briefly, when you are using a variable, are you using the value contained in the variable, or are you talking about the variable itself so that you can store something else into it? You want array that you're calling getContainsValue to have l-values for strHi, strHello, and strThere. Unfortunately there is no way to do this in Java. Initializing getContainsValue with strHi, strHello, and strThere uses the values of those variables, not their l-values.
Let's step back a bit and talk more about l-values vs values (sometimes, r-values). Consider the following code snippet:
int i = 17;
i = i + 1;
That second line is obviously not an equation; that would be nonsensical. Instead, it is an assignment. The meaning of i on the left and right sides of an assignment is different. On the right hand side, i means to use the value of that variable, in this case 17. On the left hand side, i means the variable itself, as a destination for storing values. Even though they look the same, the use of i on the right-hand side is for its value (more specifically, its r-value) and the use of i on the left-hand side is for its l-value.
In Java, there is no way to express the l-value of a variable in an array initializer, so what you're trying to do doesn't work. As others have pointed out, in other languages like C this is possible, by using the & (address-of) operator.
Since Java has limited ways of expressing l-values, usually the concept of "a place to store something into" is expressed via a reference to an object. One can then use this reference to store into fields of that object or to call methods on that object.
Suppose we have a class like this:
class MyContainer {
String str;
void setString(String s) { str = s; }
String getString() { return str; }
}
We could then rewrite your code to do something like the following:
String[] containsValue = { "hi", "hello", "there" };
MyContainer hiCont = new MyContainer();
MyContainer helloCont = new MyContainer();
MyContainer thereCont = new MyContainer();
MyContainer[] getContainsValue = { hiCont, helloCont, thereCont };
for (int x = 0; x < getContainsValue.length; x++) {
getContainsValue[x].setString(containsValue[x]);
}
Well you can use this.
Map<String,String> map = new HashMap<String,String>();
String[] str = {"hi","hello","there"};
for(int x = 0; x < str.lenght;x++){
map.put(str[x],"something you want to store");
}
Best thing may be use Map and store as key-Value pairs.
Map<String,String> myKVMap=new HashMap<String,String>();
myKVMap.put("strHi","value1");
myKVMap.put("strHello","value2");
myKVMap.put("strThere","value3");
This way you can eliminate all the variable name and value issues.
I think you should use Collection like Map.
Map is used to store data in the form of Key-Value Pairs.
Assume the Key is String and Value is a String too in your case.
Map<String, String> mp = new Map<String, String>();
mp.put("str1","Hi");
mp.put("str2","Hello");
You can iterate over it like the below.
for(Map.Entry<String, String> ar : mp.entrySet()){
System.out.println("Key: "+ar.getKey()+" :: "+"Value: "+ar.getValue());
}
Using a Map is a good idea. Another approach is to instantiate class variables, then assigning values will work.
public void testTransfer() {
String containsValue[] = { "hi", "hello", "there" };
Data strHi = new Data();
Data strHello = new Data();
Data strThere = new Data();
Data[] getContainsValue = { strHi, strHello, strThere };
for (int x = 0; x < getContainsValue.length; x++) {
getContainsValue[x].value = containsValue[x];
}
// print out
System.out.println(strHi.value);
System.out.println(strHello.value);
System.out.println(strThere.value);
}
class Data {
private String value;
}
There is no simple way to do what you want to do in Java. What you would need is the equivalent of the C / C++ address-of operator (&) ... or maybe Perl's ability to use a string as a variable name. Neither of these are supported in Java.
In theory, if the variables where instance variables, you could use reflection to access and update them. But the code to do this is messy, inefficient and fragile. And it won't work with local variables.
You would be better off looking for a different solution to the problem; e.g. use a Map, as other answers have suggested.
Or just settle for some clunky (but robust and reasonably efficient) code that uses a switch or series of if else if tests and the original variables.
If I am understanding your question, you want to be able to assign a regular String variable by looking it up in an array first and then making the assignment.
I agree with the other responders that if you are finding this approach necessary, it is probably ill-advised. But in the spirit of pure Q&A, here's the way:
interface StringAssigner {
void assign( String strValue );
}
// ...
String strHi, strHello, strThere;
StringAssigner[] asaGetContainsValue = {
new StringAssigner() { #Override public void assign( String strValue ) { strHi = strValue; } },
new StringAssigner() { #Override public void assign( String strValue ) { strHello = strValue; } },
new StringAssigner() { #Override public void assign( String strValue ) { strThere = strValue; } }
};
// ...
for (int x = 0; x < asaGetContainsValue.length; x++) {
asaGetContainsValue[x].assign( containsValue[x] );
}
Just say no.
I do agree with the other answers here that this feels like a workaround for something, but without knowing what that something is I cannot suggest anything better.
To answer the question, though: you could, however, wrap the string in simple class and store the object references of that class in your array and strHi, strHello, and strThere. This way even when you change the string property inside the class, the class object itself does not change so you will see the behavior you are looking for.
Or, you can use a HashMap as others have suggested. In your case if you still want to use the getContainsValue array, you can store the keys:
Map<String,String> map = new HashMap<String,String>();
map.put("strHi","");
map.put("strHello","");
map.put("strThere","");
String[] containsValue = { "hi", "hello", "there" };
String[] getContainsValue = { "strHi", "strHello", "strThere" };
for (int x = 0; x < getContainsValue.length; x++) {
map.put(getContainsValue[x], containsValue[x]);
}
Then, map.get("strHi") would return "hi" as you expect.
Suppose I have a custom object set up in a class similar to this.
public class anObject {
public String id, otherProperty;
public anObject(){
this.id = "1";
this.otherProperty = "cat";
}
}
Then I create an array of these objects in another class
anObject[] objects = new anObject[40];
for(int i=0; i < 40; i++){
objects[i] = new anObject();
}
What can I do then to find the first object in the array that has an id of 2 (for example)?
anObject found = null;
for(int i=0; i < 40; i++){
if ("2".equals(object[i].id)) {
// found it
found = object[i];
break; // exit the loop
}
}
Or am I missing something?
EDIT: added the break. Also, there is a convention that class names begin with an uppercase letter, such as AnObject.
There are multiple ways of going about this. First, you could do a simple for loop, iterating over all of the objects until one with a specific id is found. Your search complexity would be O(N)
anObject obj = null;
dance: for( int i = 0; i < objects.length; i++ )
{
if( object[i].id == 2 )
{
obj = object[i];
break dance;
}
}
if you know that you're always going to be searching by id, you could implement Comparable. Then you can use java.util.Arrays to sort and search the array for you. This reduces your search to O(log n)
public class anObject implements Comparable {
public String id, otherProperty;
public anObject(){
this.id = "1";
this.otherProperty = "cat";
}
public int compareTo( Object o )
{
if( o instanceof anObject )
{
return this.id.compareTo( ( (anObject) other).id );
}
return -1;
}
}
Last option, you can store the results in a Map<String, anObject>. If you're doing a lot of searching, this is the best method as it gives your search O(1), at the cost of additional memory.
There's no other way besides iterating through them and checking manually, as Matthew showed you. You can store them in the order of the id and do something like binary search to cut down time to O(log(n)) instead of O(n), but that might be too much overhead.
You can try storing them in a Map<String, YourObject> and just do map.get(id). This has O(1) access time.
Map<String, YourObject> map = new HashMap<String, YourObject>();
for (int i=0; i < 40; i++) {
YourObject obj = new YourObject(); // couldn't put anObject, it bugged me :)
map.put(obj.id, obj);
}
// get object with id = 2
YourObject secondOne = map.get("2");
if (secondOne != null) {
...
}
The best way to do this depends your main use-cases, really, and on how many elements you plan on supporting.
public static anObject findById(anObject[] arr,String str) {
for (anObject obj:arr) if (obj.id.equals(str)) return obj;
return null;
}
And then call anObject.findById(objects,"2")
Use Commons Collections:
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/CollectionUtils.html#find(java.util.Collection, org.apache.commons.collections.Predicate)
been pulling my hair for nights when I get an ClassCastException as I try to cast an object type to another object type.... I'm that sure that this code will work because all objects are are subclasses of the Object class... but somehow i get this exception...some help guys?
Sorry guys..not to be confused with the Original ArrayList, I'm learning Java programming, and practicing on casting object arrays... the exception is at line 8, where I casted the object array to a weapon array type... sorry for any form of trolling...
public class ArrayList {
public static void main(String args[]){
ArrayList arrayList = new ArrayList();
weapon[] weapons = new weapon[5];
for (int i = 0; i < weapons.length; i++) {
weapons[i] = new weapon(i);
}
weapons = (weapon[]) arrayList.add(weapons,
new weapon(weapons.length + 1, "mp5"));
}
public Object[] add(Object[] targetObjectList, Object add){
Object[] oldList = new Object[targetObjectList.length];
for (int i = 0 ; i < oldList.length; i++){
oldList[i] = targetObjectList[i];
}
Object[] newList = new Object[oldList.length+1];
for (int i = 0; i < oldList.length; i++) {
newList[i] = oldList[i];
}
newList[newList.length - 1] = add;
return newList;
}
}//end arrayList class
class weapon {
String name;
int id;
public weapon(int id) {
this.id = id;
name = "weapon";
}
public weapon(int id, String name) {
this.id = id;
this.name = name;
}
}
A weapon is an Object; therefore, we may treat an array af weapon as an array of Object. However, not all Objects are weapons; therefore, an array of Object may not be treated as an array of weapon - unless the thing that seems to be an array of Object really was an array of weapon to begin with. Because of this, your approach will become difficult. You could try
weapon[] newList = new weapon[oldList.length+1];
instead, but then you'd need to change all the arrays to be of type weapon[], and the method wouldn't become general (which I suppose is your goal). However, if you want a method that can add an element to any kind of array, you should use generics instead.
P.S. If you are learning about programming and arrays, writing such an add() method is a good exercise - but if you "just want it to work", you should use ArrayList instead, which does the whole job of adding for you.
Arrays are subclass of Object. You cannot cast Object[] to weapon[]. Here is a generic solution for your problem:
weapons = (weapon[])arrayList.add(weapons, new weapon(weapons.length + 1, "mp5"),weapon.class);
and
public <T> T[] add(T[] targetObjectList, T add, Class<T> c) {
T[] oldList = (T[])Array.newInstance(c, targetObjectList.length);
for (int i = 0; i < oldList.length; i++) {
oldList[i] = targetObjectList[i];
}
T[] newList = (T[])Array.newInstance(c, oldList.length + 1);
for (int i = 0; i < oldList.length; i++) {
newList[i] = oldList[i];
}
newList[newList.length - 1] = add;
return newList;
}
Note: Follow Java Naming Conventions. Avoid Java API class names as your class name.
Object o = new Foo(). This is ok. Foo is Object. As Foo extends Object
Foo f = new Object() Not OK, Object is NOT Foo (Compiler error)
Foo f = (Foo)(new Object()) - ClassCastException
What you are looking to do is known as contravariance. The inherent problem in your code is that the ARRAY that you are creating in the add function is an array of Objects (regardless of what those objects actually end up being).
If you were to do the following, your code would work. I think that you need to work with generics in this sense (look at the actual ArrayList class as it already implements this).
...snip...
Object[] newList = new weapon[oldList.length+1];
for (int i = 0; i < oldList.length; i++) {
newList[i] = oldList[i];
}
newList[newList.length - 1] = add;
return newList;
...snip...
Here is an example code that works much as you are anticipating it.
public class Test {
public static void main(String[] args) {
Integer[] i = (Integer[])Test.get();
}
public static Object[] get() {
Object[] o = new Object[20];
for(int i = 0; i < o.length; i++) {
o[i] = new Integer(0);
}
return o;
}
}
ArrayList has a method .toArray(T[] a) which will return an enlarged array size containing your new elements. So how does this contravariance work? Through the java.reflect package. This is a very small excerpt of how it actually works (there are a few different packages that work together to get down to this.)
...Object[] function(Object[] typearray) {
Object[] o = (Object[]) Array.newInstance(typearray.getClass().getComponentType(), 20);
...fill...
return o;
}
The reason you must do this is because the ARRAY (since everything in Java are objects) is an Object itself, then an Array of Objects cannot be upcasted to an Array of Integers. If you downcasted from an array of integers to an array of Objects (as shown in this above example) and then upcasted again from an Array of Objects to an Array of Integers (because we never dropped the underlying class of it being an Array of Integers).