this is my situation:
I have a method which has a String as parameter. This method has to receive an object from a class called Urls. The object it has to recieve, has the same name as the value of the String. Here is my code:
private Object getObject(String objectName){
try
{
Field field = Urls.class.getField(objectName);
}
catch (NoSuchFieldException e)
{}
catch (IllegalAccessException e)
{}
}
And here is my Urls class:
public class Urls{
public static final String[] ASTUN = new String[]{
"http://www.astun.com/camara/truchas.jpg",
"https://www.todonieve.com/photowebcam.asp?fotografia=astun/astun.jpg",
"http://www.astun.com/camara/caba%C3%B1a%20sarrios.jpg",
"http://www.astun.com/camara/sector%20sarrios.jpg",
"http://www.astun.com/camara/sector%20raca%20prad.jpg",
"http://www.astun.com/camara/sector%20aguila%20cr.jpg",
"http://www.astun.com/camara/sector%20truchas.jpg",
"http://www.astun.com/camara/sector%20llanos%20.jpg",
"http://www.astun.com/camara/llegada.jpg",
"http://www.astun.com/camara/terraza.jpg",
"http://www.astun.com/camara/panoramica.jpg",
"http://www.astun.com/camara/snow.jpg"
};
private static final String[] CANDANCHU = new String[]{
"https://www.todonieve.com/photowebcam.asp?fotografia=candanchu/candanchu.jpg",
"https://www.todonieve.com/photowebcam.asp?fotografia=CandanchuNew/CandanchuNew.jpg",
"https://www.todonieve.com/photowebcam.asp?fotografia=candanchu_rinconada/candanchu_rinco.jpg",
"https://www.todonieve.com/photowebcam.asp?fotografia=candanchu_tobazo/candanchu_tobazo.jpg"
};
}
So, that way I have a Field object, but how can I get the String[] of that field? I have read about the get(Object object) method of Field class but it seems that i doesnt do what I want to do...
EDIT: I WANT TO GET ASTUN OR CANDACHU STRING ARRAYS
Avoid reflection whenever possible. It often does more harm than good.
Put them into a map:
public class Urls {
//put your arrays here
private static final Map<String,String[]> urlsLists = new HashMap<>();
static {
urlLists.put("ASTUN", ASTUN);
urlLists.put("CANDANCHU", CANDANCHU);
}
public static String[] getUrlList(String name) {
return urlLists.get(name);
}
}
And then call it like this:
private Object getObject(String objectName){
return Urls.getUrlList(objectName);
}
Update
You loose a lot of nice stuff Java help you with, including type-safety, encapsulation and compile-time checks. Because of this it is a lot more error prone. There is a much increased risk of run-time errors and you need a bunch of extra code to handle this. Your brief example already have two catch clauses. Trust me - that will just get worse.
You can even improve type-safety more by creating an Enum to define url-types. Then you will get compile time checks that you have spelled the name right and even auto-completion all through-out your code. :)
public class Urls {
public enum UrlTypes {ASTUN; CANDANCHU;}
// ..
private static final Map<UrlTypes,String[]> urlsLists = new HashMap<>();
static {
urlLists.put(UrlTypes.ASTUN, ASTUN);
urlLists.put(UrlTypes.CANDANCHU, CANDANCHU);
}
..
public static String[] getUrlList(UrlTypes name) {
return urlLists.get(name);
}
}
Every error you can catch at compile-time instead of at run-time can save you between half an hour or half a week of work, when things get complex.
You will need something like this:
private Object getObject(String objectName){
try
{
Field field = Urls.class.getField(objectName);
Object o = field.get(null); // null works as well.
return o;
}
catch (NoSuchFieldException e)
{ e.printStackTrace(); }
catch (IllegalAccessException e)
{ e.printStackTrace(); }
}
private String[] getStringArray(String arrayName)
{
return (String[]) getObject(arrayName);
}
Usage:
Object o = getObject("ASTUN");
// or:
String[] arr = getStringArray("ASTUN");
getField will return the (reflexive) representation of the field concept in the Urls class. You then need to bind it with an actual object of that class to have access to the contents of the field in that object.
Field field = Urls.class.getFiled(objectName);
String[] values = (String[]) field.get(o);
where o is a variable of type Urls.
Note the cast to String[] as Field.get() will return an Object as it does not know the actual type. You should make sure that the type is indeed correct by using the Field.getType() method and compare that to Urls.class.
Since objects dont have names, you will need to create ypur own class, put the field variable in there, then use that instead of Object. Or use a Map for better key/value logging.
Related
I need to use variables initialized in outer class to be used in inner class.So I had used static variables.Also this is Flink application.
When built as eclipse-export-runnable jar --it works fine--state of variable retains
When built as maven or eclipse-export-jar--it fails--state of variable lost
FileMonitorWrapper.fileInputDir--values is "" and don't fetch the passed value.
Sounds strange..any thoughts
static transient String fileInputDir="";
static transient String fileArchiveDir="";
#SuppressWarnings("serial")
public DataStream<String> ScanDirectoryForFile(String inputDir, String inputFilePattern,String archiveDir, StreamExecutionEnvironment env) {
try {
FileMonitorWrapper.fileArchiveDir = archiveDir;
FileMonitorWrapper.fileInputDir = inputDir;
filteredDirFiles = dirFiles.filter(new FileMapper());
.
.
.
}
}
#SuppressWarnings("serial")
static class FileMapper implements FilterFunction<TimestampedFileInputSplit>{
#Override
public boolean filter(TimestampedFileInputSplit value) throws Exception {
if(value.toString().contains("done"))
FileMonitorWrapper.doneFound = true;
if(value.toString().contains("dat"));
FileMonitorWrapper.datFound = true;
if(FileMonitorWrapper.datFound && FileMonitorWrapper.doneFound) {
try {
if(value.getPath().toString().contains("done")) {
Files.move(Paths.get(FileMonitorWrapper.fileInputDir+"\\"+value.getPath().getName()),
Paths.get(FileMonitorWrapper.fileArchiveDir+"\\"+value.getPath().getName()));
}
}catch(Exception e){
e.printStackTrace();
}
return (!value.toString().contains("done"));
}
else
return false;
}
}
}
Generally speaking, serialization of POJOs does not capture the state of static variables. From what I have read about it, Flink serialization is no different.
So when you say that the static variable state is "retained" in some cases, I think you are misinterpreting the evidence. Something else is preserving the state of the static variables OR they are being initialized to the values that happen to be the same in the "before" and "after" cases.
Why am I so sure about this? The issue is that serializing static variables doesn't make much sense. Consider this
public class Cat {
private static List<Cat> allCats = new ArrayList<>();
private String name;
private String colour;
public Cat(...) {
...
allCats.add(this);
}
...
}
Cat fluffy = new Cat("fluffy", ...);
Cat claus = new Cat("claus", ...);
If the static field of Cat is serialized:
Every time a serial stream contains a Cat it will (must) contain all cats created so far.
Whenever I deserialize a stream contains a Cat, I also need to deserialize the ArrayList<Cat>. What do I do with it?
Do I overwrite allCats with it? (And lose track of the other cats?)
Do I throw it away?
Do I try to merge the lists? (How? What semantics? Do I get two cats called "fluffy"?)
Basically, there is no semantic for this scenario that is going to work out well in general. The (universal) solution is to NOT serialize static variables.
I'm very new to Java so it makes it hard for me to explain what I'm trying to do.
I have an abstract class that invokes several object constants like this:
public abstract class Enchantment implements Keyed {
/**
* Provides protection against environmental damage
*/
public static final Enchantment PROTECTION_ENVIRONMENTAL = new EnchantmentWrapper("protection");
In a different file I can access this perfectly fine with Enchantment value = Enchantment.PROTECTION_ENVIRONMENTAL;
However, I'm trying to use a string variable for this instead. Something like this:
String str = "PROTECTION_ENVIRONMENTAL";
Enchantment value = Enchantment.str;
Obviously that won't work. So I did a bunch of research and learned I need to use reflection for this. Using this source code's docs I figured I was looking for field data. So I tried both:
Field fld = Enchantment.class.getField("PROTECTION_ENVIRONMENTAL");
Field fld = Enchantment.class.getDeclaredField("PROTECTION_ENVIRONMENTAL");
But these returned me a NoSuchFieldException. As I was on it, I've tried both getMethod() and getDeclaredMethod() just as well equally with no luck.
I'm now at the point that these are probably "object constants"? I'm not sure how to call them. But I'm definitely at a loss on how to get this to work now and after everything I've tried myself, I figured it was time to ask for some help here.
That one comment is spot on: you absolutely do not use reflection here.
There are only two valid reasons to use reflection:
you are creating a framework that has to deal with classes it doesn't know about
you have for some other reason to deal with classes you don't know about at compile time
But your code perfectly knows about that Enchantment class, its capabilities, and so on. Therefore reflection is the wrong approach. You figured it yourself: it is damn hard to get right, and damn right to get it wrong in some subtle ways. And when you get it wrong, it always blows up at runtime. Reflection code compiling means nothing. It always waits for you to run it to throw up in your face.
So to answer your question by not answering it: use a Map. Like:
Map<String, Enchantment> enchantmentsByConstantName = new HashMap<>();
enchantmentsByConstantName.put("PROTECTION_ENVIRONMENTAL", PROTECTION_ENVIRONMENTAL);
Alternatively, these constants could go into an enum, as outlined in the other answer, but in a sightly different way:
enum EnchantmentHolder {
PROTECTION_ENVIRONMENTAL(new EnchantmentWrapper("protection")),
ANOTHER_ENCHANTMENT(...)
A_THIRD_ENCHANTMENT(...)
...;
private Enchantment enchantment;
private EnchantmentHolder(Enchantment enchantment) {
this.entchantment = entchantment;
}
public Enchantment getEntchantment() { return entchantment; }
You may want to look into enumerations if you know they're going to be constant values;
public enum Enchantment {
PROTECTION_ENVIRONMENTAL {
public void cast() {
// do enum-specific stuff here
}
},
ANOTHER_ENCHANTMENT {
public void cast() {
// do enum-specific stuff here
}
},
A_THIRD_ENCHANTMENT{
public void cast() {
// do enum-specific stuff here
}
};
public abstract void cast();
}
enums can be treated like classes and have methods and properties. You can also convert to and from strings Enchantment.valueOf("PROTECTION_ENVIRONMENTAL") but that's generally if you are reading from a configuration file - in code you'd reference the value directly.
Once you have the Field, you need to call Field.get(Object) with an instance (in this case the class). Something like,
Class<?> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
System.out.println(f.get(cls));
} catch (Exception e) {
e.printStackTrace();
}
Since you want the Enchantment, you could then test that the instance you get is assignable to Enchantment. Something like,
Class<? extends Enchantment> cls = Enchantment.class;
try {
Field f = cls.getField("PROTECTION_ENVIRONMENTAL");
Object obj = f.get(cls);
if (cls.isAssignableFrom(obj.getClass())) {
Enchantment e = cls.cast(obj);
System.out.println(e);
}
} catch (Exception e) {
e.printStackTrace();
}
But the enum approach is better.
I am trying to get the name of variable in android using java.
The variable has a annotation, and I want to get the variable's name with the annotation's name. is this possible?
just like this,
#getnameofthisfield
private String name;
use getnameofthisfield and get name
You can do it like this:
Class<YourClass> clazz = // somehow get a reference to the class that contains the field
Field[] fields = clazz.getDeclaredFields();
List<String> fieldNames = new LinkedList<>();
for (Field field : fields) {
if (field.isAnnotationPresent(#getnameofthisfield.class)) {
fieldNames.add(field.getName);
}
}
In the end fieldNames will contain the names of all fields, annotated with #getnameofthisfield.
This comes up when you have a Data holder class that is a model for Firebase fields (for example) and the spelling of the member names must exactly equal the Strings in the Firebase tree. While I have not eliminated the duplicate typing/spelling of the Strings/fields, this will at least detect these programming errors at run-time.
public class User {
private String email;
private String name;
// avoid out-of-sync String names of fields in other files
public static String getFieldName(String fieldRequest) {
try {
return User.class.getDeclaredField(fieldRequest).getName();
} catch (NoSuchFieldException e1) {
e1.printStackTrace();
throw new RuntimeException("Unrecognized field in "
+ User.class.getSimpleName() + ", (" + fieldRequest + ")"); }
}
Here is an example usage:
// demonstration of how the getFieldName() protects against mistakes...
String userNameField = User.getFieldName("name"); // this works
String userEmailField = User.getFieldName("userEmail"); // this throws an error
Get annotation value
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class Util{
#SuppressWarnings("unchecked")
public static<T> T getAnnotationValue(Class<?> clazz,Class<? extends Annotation> annotationClass,String element) throws Exception {
Annotation annotation = clazz.getAnnotation(annotationClass);
Method method = annotationClass.getMethod(element,(Class[])null);
if (annotation == null)
return((T)method.getDefaultValue());
return((T)method.invoke(annotation,(Object[])null));
}
}
In my understanding that isnt possible, the java compiler doesn't save variable names. What is it that your trying to do with such name?
How can I iterate over the attributes of an object, with the attribute names provided in a list/array - NOT all attributes, like using reflection & getDeclaredFields().
public class MyClass
{
public type1 att1;
public type2 att2;
public type3 att3;
public MyClass(
att1="helo";
att2="bye";
att3="morning";
);
...
public void function()
{
String myStrings[];
myStrings = new String[] { "att2", "att3" };
MyClass myobject = new MyClass();
for(var in myStrings)
{
System.out.println(var);
System.out.println(myobject.var);
System.out.println();
}
}
}
Your question is somewhat ambiguous about using reflection. If you are OK with reflection, but want specific fields only without iterating over getDeclaredFields(), then the following code should work for you:
for (String var : myStrings) {
Field field = MyClass.class.getDeclaredField(var);
field.setAccessible(true);
System.out.println(var);
System.out.println(field.get(myObject));
System.out.println();
}
Note that this code works for private fields, too. Also, keep in mind that you'll have to handle exception associated with the reflection calls.
UPDATE: Exceptions thrown in this code.
MyClass.class.getDeclaredField(var) declares a checked NoSuchFieldException. You must handle it because obviously there is no mechanism to make sure that the fields in myString match an actual implementation of MyClass.
field.get(myObject) throws a checked IllegalAccessException if the field is inaccessible. Which it should not be because of field.setAccessible(true), but you still have to catch or re-throw the exception.
There are also unchecked exceptions you may want to handle. See the javadoc for details
java.lang.Class.getDeclaredField(String)
java.lang.reflect.AccessibleObject.setAccessible(boolean) inherited by java.lang.reflect.Field
java.lang.reflect.Field.get(Object)
You probably want to use some technology that builds on top of JavaBeans / BeanInfo. Apache Commons / BeanUtils is a good starting point here.
Please refer to this previous answer of mine for more info:
https://stackoverflow.com/a/5856982/342852
But if you just want to use fields, not bean properties, here's a Java 8 method to do so:
public static Map<String, Object> getFieldProperties(Object o, Collection<String> fields) {
Class<?> type = o.getClass();
return fields.stream().map(n -> {
try {
return type.getDeclaredField(n);
} catch (NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}).collect(Collectors
.toMap(
(Function<Field, String>) Field::getName,
(Function<Field, Object>) field -> {
try {
field.setAccessible(true);
return field.get(o);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}));
}
Unfortunately the checked exceptions make this more verbose than it would need to be.
How can I invoke private method using method handles ?
As far as I can see there are only two kinds of publicly accessible Lookup instances:
MethodHandles.lookup()
MethodHandles.publicLookup()
and neither allows unrestricted private access.
There is the non-public Lookup.IMPL_LOOKUP that does what I want. Is there some public way to obtain it (assuming that SecurityManager allows it) ?
Turns out it's possible with Lookup#unreflect(Method) and temporarily making method accessible (potentially introducing small security issue unless done during program initialization).
Here is modified main method from Thorben's answer:
public static void main(String[] args) {
Lookup lookup = MethodHandles.lookup();
NestedTestClass ntc = new Program().new NestedTestClass();
try {
// Grab method using normal reflection and make it accessible
Method pm = NestedTestClass.class.getDeclaredMethod("gimmeTheAnswer");
pm.setAccessible(true);
// Now convert reflected method into method handle
MethodHandle pmh = lookup.unreflect(pm);
System.out.println("reflection:" + pm.invoke(ntc));
// We can now revoke access to original method
pm.setAccessible(false);
// And yet the method handle still works!
System.out.println("handle:" + pmh.invoke(ntc));
// While reflection is now denied again (throws exception)
System.out.println("reflection:" + pm.invoke(ntc));
} catch (Throwable e) {
e.printStackTrace();
}
}
I don't know, if this is what you really want. Perhaps you could give some more information about what you want to achieve with it.
But if you want to access Lookup.IMPL_LOOKUP, you can do it like in this code sample:
public class Main {
public static void main(String[] args) {
Lookup myLookup = MethodHandles.lookup(); // the Lookup which should be trusted
NestedTestClass ntc = new Main().new NestedTestClass(); // test class instance
try {
Field impl_lookup = Lookup.class.getDeclaredField("IMPL_LOOKUP"); // get the required field via reflections
impl_lookup.setAccessible(true); // set it accessible
Lookup lutrusted = (Lookup) impl_lookup.get(myLookup); // get the value of IMPL_LOOKUP from the Lookup instance and save it in a new Lookup object
// test the trusted Lookup
MethodHandle pmh = lutrusted.findVirtual(NestedTestClass.class, "gimmeTheAnswer", MethodType.methodType(int.class));
System.out.println(pmh.invoke(ntc));
} catch (Throwable e) {
e.printStackTrace();
}
}
// nested class with private method for testing
class NestedTestClass{
#SuppressWarnings("unused")
private int gimmeTheAnswer(){
return 42;
}
}
}
It works with JDK 7, but could break in JDK 8. And be cautious! My antivirus gave an alarm when I executed it.
I think there isn't a public or clean way to do it.
I had a similar issue and finally found a solution: Access non-public (java-native) classes from JDK (7).
Here's a similiar solution which includes arguments in a private
function (I just happened to have the code lying around from a previous project):
class name:
InspectionTree.java
function signature:
private String getSamePackagePathAndName(String className, String classPath)
String firstName = "John";
String lastName = "Smith";
//call the class's constructor to set up the instance, before calling the private function
InspectionTree inspectionTree = new InspectionTree(firstName, lastName);
String privateMethodName ="getSamePackagePathAndName";
Class[] privateMethodArgClasses = new Class[] { String.class, String.class };
Method method =
inspectionTree.getClass().getDeclaredMethod(privateMethodName, privateArgClasses);
method.setAccessible(true);
String className = "Person";
String classPath = "C:\\workspace";
Object[] params = new Object[]{className, classPath};
//note the return type of function 'getSamePackagePathAndName' is a String, so we cast
//the return type here as a string
String answer= (String)method.invoke(inspectionTree, params);
method.setAccessible(false);