I am trying to loop through many ArrayLists of custom classes with the same code and thought using reflection would make it easier. I am running into an issue when I try to get a reference to each field however. Here is a small representation of the code I am trying to run. (my code is different but the essentials are there):
import java.lang.reflect.*;
import java.util.ArrayList;
public class Stack {
public ArrayList<Custom1> cust11;
public ArrayList<Custom1> cust12;
public ArrayList<Custom1> cust13;
public ArrayList<Custom2> cust21;
public ArrayList<Custom2> cust22;
public ArrayList<Custom2> cust23;
public static void main(String args[]) {
Stack stack = new Stack();
}
public Stack() {
cust11 = new ArrayList<Custom1>();
cust12 = new ArrayList<Custom1>();
cust13 = new ArrayList<Custom1>();
cust21 = new ArrayList<Custom2>();
cust22 = new ArrayList<Custom2>();
cust23 = new ArrayList<Custom2>();
doReflect();
}
public void doReflect(){
Field[] fields = this.getClass().getFields();
for(Field f : fields) {
if(f.getName().contains("cust1")) {
try {
ArrayList<Custom1> temp = (ArrayList<Custom1>)f.get(cust11);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
class Custom1{
public Custom1() {}
}
class Custom2{
public Custom2() {}
}
When it gets to
ArrayList<Custom1> temp = (ArrayList<Custom1>)f.get(cust11);
I get
java.lang.IllegalArgumentException: Can not set java.util.ArrayList field
Stack.cust11 to java.util.ArrayList
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(Unknown Source)
at sun.reflect.UnsafeObjectFieldAccessorImpl.get(Unknown Source)
at java.lang.reflect.Field.get(Unknown Source)
at Stack.doReflect(Stack.java:33)
at Stack.<init>(Stack.java:25)
at Stack.main(Stack.java:14)
How can I do this?
I should have used
ArrayList<Custom1> temp = (ArrayList<Custom1>)f.get(this);
Related
I created an ArrayList to store Classes. Then I would like to use the members in the ArrayList to create an instance. How can I instantiate?
import java.util.ArrayList;
class Machine{
public String toString(){
return "I am a machine";
}
}
class MyMachine extends Machine{
public String toString(){
return "This is a super Machine";
}
}
class MyClass<T> {
public static <T> void showString(T abc){
System.out.println(abc);
}
}
public class myfun {
public static void main(String[] args) {
ArrayList<Class> hahalist = new ArrayList<>();
hahalist.add(MyClass.class);
hahalist.add(Machine.class);
hahalist.add(MyClass.class);
// Machine abc = new Machine();
// I can't do this
// abc = hahalist.get(1);
// I can't do this either
// hahalist.get(1) abc = new hahalist.get(1)()
}
}
Instead of storing classes in the list, you could store references to the constructors:
List<Supplier<Object>> hahalist = new ArrayList<>();
hahalist.add(MyClass::new);
hahalist.add(Machine::new);
hahalist.add(MyMachine::new);
Object instance = hahalist.get(1).get(); // Calling constructor
Because MyClass and Machine have a closest common ancestor of Object that is the return type of the Supplier I'm using here.
If you have a no args constructor on all you classes you can just use the Class.newInstance(), eg
try {
Object x = hahaList.get(0).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
(I haven't got my IDE open so haven't verified the exact syntax).
If you've got arguments to the constructor then its a bit more convoluted. You can read the full details here: https://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html
I get the following exception stack trace:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851) at
Controllers.Manager.createTheNetwork(Manager.java:123) at
Controllers.Main.main(Main.java:53)
Why do I get this stack trace? I've just initialized an ArrayList from another class.
Main.java
public class Main {
private static Manager anager;
private static ArrayList<Vfoo> aVfooList;
private static ArrayList<Pfoo> aPfooList;
private static ArrayList<Nfoo> aNfooList;
public static void main(String[] args) {
aPfooList = new ArrayList<Pfoo>();
aVfooList = new ArrayList<Vfoo>();
aNfooList = new ArrayList<Nfoo>();
aPfooList.add(new Pfoo());
aPfooList.add(new Pfoo());
for (Pfoo p : aPfooList)
if (!p.isAMethod())
aVfooList.add(new Cfoo());
aNfooList.add(new Nfoo());
aNfooList.add(new Nfoo());
manager = new Manager();
manager.setList(aList1);
manager.setList(aList2);
manager.createSomething();
}
}
Manager.java
import java.util.ArrayList;
public class Manager {
private ArrayList<Nfoo> nfooList = new ArrayList<Nfoo>();
private ArrayList<Vfoo> vfooList = new ArrayList<Vfoo>();
public NodeManager (){
}
public ArrayList<Nfoo> getNfooList(){
return nfooList;
}
public void addNfooToTheNfooNetwork(Nfoo n){
this.nfooList.add(n);
System.out.println(n);
}
public void addANewVfooToTheNetwork(Vfoo aVfoo){
getNfooList().get(0).addToStack(aVfoo);
}
public void addANewTfooToTheNetwork(Tfoo t, int whichNfoo){
getNfooList().get(whichNfoo).addToStack(t);
}
public void resetNfooNetwork(){
nfooList.clear();
}
public void createSomething(){
for (Nfoo n : nfooList)
addNfooToTheNfooNetwork(n);
for (Vfoo v : vfooList)
addANewVfooToTheNetwork(v);
}
public void createSomething(ArrayList<Nfoo> nl, ArrayList<Vfoo> vl){
this.setNfooList(nl);
this.setVfooList(vl);
for (Nfoo n : nfooList)
addNfooToTheNfooNetwork(n);
for (Vfoo v : vfooList)
addANewVfooToTheNetwork(v);
}
public void setNfooList(ArrayList<Nfoo> nl){
this.nfooList = nl;
}
public void setVfooList(ArrayList<Vfoo> vl){
this.vfooList = vl;
}
}
Indeed, as it is written in the comments you are using Iterators. Check the documentation for the for looping : for (Obj o : objectList)
As #Nagesh Susaria said in the comments, your actual mistake is that you are trying to re-initialize or just add new Nfoo Objects into your ArrayList while the Iterator from the loop is parsing it.
Check the following:
for (Nfoo n : nfooList)
addNfooToTheNfooNetwork(n);
and
public void addNfooToTheNfooNetwork(Nfoo n){
this.nfooList.add(n);
System.out.println(n);
}
As you will see, the "glitch" of your code is in the this.nfooList.add(n); or addNfooToTheNfooNetwork(n); .
Both of them are doing the same I guess. Delete one of them and you are ok!
after reading for serialization, i tried to perform an experiment on the example provided in the book. Following code has some variation and this is basically picked from SCJP book.
import java.io.FileInputStream;
public class SerializationTest {
public static void main(String[] args) {
Collar c = new Collar(4);
Dog d = new Dog(c, "Sheru", 32);
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(
"C:\\Users\\dell\\Desktop\\NewDir\\DogState.txt");
oos = new ObjectOutputStream(fos);
oos.writeObject(d);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
oos.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// ***************************************************************************************************
// //
Dog restore = null;
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(
"C:\\Users\\dell\\Desktop\\NewDir\\DogState.txt");
ois = new ObjectInputStream(fis);
restore = (Dog) ois.readObject();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
fis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
System.out.println("after: dog name: "+ restore.name +" , collar=" + restore.getCollar());
System.out.println("Animal material is:" + restore.getWeight());
}
}
// Intentionally added parameterized constructor so that default constructor is not called.
class Animal{
int weight = 42;
public Animal(int weight) {
this.weight = weight;
System.out.println("animal constructor");
}
}
class Dog extends Animal implements Serializable {
String name;
transient Collar collar;
public Collar getCollar() {
return collar;
}
public void setCollar(Collar collar) {
this.collar = collar;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public Dog(Collar collar, String name, int weight) {
super(weight);
System.out.println("Dog constructor");
this.collar = collar;
this.name = name;
}
}
class Collar {
int size;
public Collar(int size) {
System.out.println("Collar constructor");
this.size = size;
}
}
Here my question is why InvalidClassException is occuring, Please explain what is the root cause of exception.
Current output is
Collar constructor
animal constructor
Dog constructor
java.io.InvalidClassException: Dog; Dog; no valid constructor
at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:39)
Caused by: java.io.InvalidClassException: Dog; no valid constructor
at java.io.ObjectStreamClass.<init>(Unknown Source)
at java.io.ObjectStreamClass.lookup(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:18)
Exception in thread "main" java.lang.NullPointerException
at SerializationTest.main(SerializationTest.java:54)
If i remove Animal constructor and comment out the super(weight) in Dog constructor, then output is
Collar constructor
Dog constructor
after: dog name: Sheru , collar=null
Animal material is:42
I understand this output, and i also get the fact that during de-serialization serialzable class's superclass constructor is called but here no default constructor is present, so exception occured. But why this exception occur i want to know.
The exception is thrown by the time you try to read the from the file:
at java.io.ObjectInputStream.readObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:39)
The stack trace clearly indicates that your program aborts when it attempts to read an object. What may get you confused is the second stack trace referring to the write:
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at SerializationTest.main(SerializationTest.java:18)
But you seem to have skipped this very important line:
Caused by: java.io.InvalidClassException: Dog; no valid constructor
Java stacktraces can be nested, one exception can lead to another; and this here is a litte ackward. As a matter of fact, during the serialization of an object it is already computed that there is no default constructor. Here's an excerpt of the involved source code:
...
cons = getSerializableConstructor(cl);
...
} else if (cons == null) {
deserializeEx = new InvalidClassException(name, "no valid constructor");
}
This means that during the write, it is already clear that there is no valid constructor. However, the exception is not thrown but serialized along with the object. Later, when deserializing, this code is called:
void checkDeserialize() throws InvalidClassException {
if (deserializeEx != null) {
InvalidClassException ice =
new InvalidClassException(deserializeEx.classname,
deserializeEx.getMessage());
ice.initCause(deserializeEx);
throw ice;
}
}
Here, a "real" exception is thrown, but the cause of it is set to be the one stored during serialization of the object.
This mechanism is only found in SUN/Oracle's Java implementation; OpenJDK clearly throws an exception by the time a read is attempted, and does not keep around a stack trace from writing.
The non-Serializable base class Dog must have an accessible default constructor. When you comment out the Dog(weight) constructor, you force the compiler to provide one, and when you leave it in the compiler doesn't provide one.
There is a rule that the parent class of the serializing class or any associated class to that must be implementing Serializable.In your case, when you remove thesuper(weight); then it checks for default constructor and runs properlyBut also if you put class Animal implements Serializable then also code runs properly.
I have been trying to get this to work for a while and I have searched high and low for answers (including here). But I can't figure it out.
So here the code (a bit minimized):
public class Event<T> {
public MessageType msgType;
public int layer;
public int gameObjectId;
public String name;
public float value;
public int senderId;
public T ext;
public Event(Class<T> classType) {
if(classType != null) {
try {
ext = classType.newInstance(); //this is the failing line
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
msgType = MessageType.NONE;
layer = TypeHandler.lAll;
gameObjectId = -1;
name = null;
value = -1;
}
}
public class DataExtension {
public float[] values;
public String[] string;
public Object[] objects;
public boolean[] boolens;
}
This is the problem:
Event<DataExtension> mSelectionEvent;
mSelectionEvent = new Event<DataExtension>(DataExtension.class, MessageType.SELECTED);
This give me the following exception:
java.lang.InstantiationException: se.plainentertainment.bagl.v2.core.Event$DataExtension
at java.lang.Class.newInstance0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at se.plainentertainment.bagl.v2.core.Event.<init>(Event.java:85)
at se.plainentertainment.bagl.v2.core.SelectionHandler.<init>(SelectionHandler.java:46)...
So, the class that I'm trying to instantiate should meet all the requirements, right? It has a default constructor and it's not abstract in any way. Why do I get this error?
Thanks in advance!
Event$DataExtension
This is an inner class. Is it static? I'm going to go with a hunch and say no. Which means it contains a reference to the containing Event (implicitly), which means you likely can't instantiate it via newInstance.
Try setting it to be a static inner class, or move it out of the class it if it doesn't need to be tied to the event object.
I know that we can access private constructor via reflection as #Sanjay T. Sharma mentioned in his answer of my question: Does “instanceof Void” always return false?
However, #duffymo said:
you can access private everything with reflection - methods, constructors, data members, everything.
How can I access the private methods and the private data members?
Is it possible to access local variable via reflection?
Is there a way to prevent anyone from accessing private constructors, methods, and data members?
1) How can I access the private methods and the private data members?
You can do it with a little help of the setAccessible(true) method:
class Dummy{
private void foo(){
System.out.println("hello foo()");
}
private int i = 10;
}
class Test{
public static void main(String[] args) throws Exception {
Dummy d = new Dummy();
/*--- [INVOKING PRIVATE METHOD] ---*/
Method m = Dummy.class.getDeclaredMethod("foo");
//m.invoke(d); // Exception java.lang.IllegalAccessException
m.setAccessible(true);//Abracadabra
m.invoke(d); // Now it's OK
/*--- [GETING VALUE FROM PRIVATE FIELD] ---*/
Field f = Dummy.class.getDeclaredField("i");
//System.out.println(f.get(d)); // Not accessible now
f.setAccessible(true); // Abracadabra
System.out.println(f.get(d)); // Now it's OK
/*--- [SETTING VALUE OF PRIVATE FIELD] ---*/
Field f2 = Dummy.class.getDeclaredField("i");
//f2.set(d,20); // Not accessible now
f2.setAccessible(true); // Abracadabra
f2.set(d, 20); // Now it's OK
System.out.println(f2.get(d));
}
}
2) Is it possible to access a local variable via reflection?
No. Local variables cannot be accessed outside of a block in which they were created (someone could say that you can assign such a variable to a field like field = localVariable; and later access such a field via reflection, but this way we will be accessing the value, not the variable).
3) Is there any way to prevent anyone from accessing private constructors, methods, and data members?
I think for constructors or methods you could use stacktrace to check if it was invoked by Reflection.
For fields I can't find a solution to prevent accessing them via reflection.
[WARNING: This is not approved by anyone. I just wrote it inspired by your question.]
class Dummy {
private void safeMethod() {
StackTraceElement[] st = new Exception().getStackTrace();
// If a method was invoked by reflection, the stack trace would be similar
// to something like this:
/*
java.lang.Exception
at package1.b.Dummy.safeMethod(SomeClass.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
-> at java.lang.reflect.Method.invoke(Method.java:601)
at package1.b.Test.main(SomeClass.java:65)
*/
//5th line marked by "->" is interesting one so I will try to use that info
if (st.length > 5 &&
st[4].getClassName().equals("java.lang.reflect.Method"))
throw new RuntimeException("safeMethod() is accessible only by Dummy object");
// Now normal code of method
System.out.println("code of safe method");
}
// I will check if it is possible to normally use that method inside this class
public void trySafeMethod(){
safeMethod();
}
Dummy() {
safeMethod();
}
}
class Dummy1 extends Dummy {}
class Test {
public static void main(String[] args) throws Exception {
Dummy1 d1 = new Dummy1(); // safeMethod can be invoked inside a superclass constructor
d1.trySafeMethod(); // safeMethod can be invoked inside other Dummy class methods
System.out.println("-------------------");
// Let's check if it is possible to invoke it via reflection
Method m2 = Dummy.class.getDeclaredMethod("safeMethod");
// m.invoke(d);//exception java.lang.IllegalAccessException
m2.setAccessible(true);
m2.invoke(d1);
}
}
Output from Test main method:
code of safe method
code of safe method
-------------------
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:601)
at package1.b.Test.main(MyClass2.java:87)
Caused by: java.lang.RuntimeException: method safeMethod() is accessible only by Dummy object
at package1.b.Dummy.safeMethod(MyClass2.java:54)
... 5 more
Using the method shown in the answer you linked to: setAccessible(true), which is a method of the superclass of Field, Constructor and Method.
No.
No, unless the code runs in a JVM you control, where you install a security manager. But if you give someone a jar file, and he uses the classes from this jar file, he'll be able to access everything.
To access a private field you will need to call the
Class.getDeclaredField(String name) or enter code here method.
Check this simple code:
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Field privateStringField = PrivateObject.class.
getDeclaredField("privateString");
privateStringField.setAccessible(true);
String fieldValue = (String) privateStringField.get(privateObject);
System.out.println("fieldValue = " + fieldValue
To access a private method you will need to call the Class.getDeclaredMethod(String name, Class[] parameterTypes) or Class.getDeclaredMethods() method.
Check this simple code:
public class PrivateObject {
private String privateString = null;
public PrivateObject(String privateString) {
this.privateString = privateString;
}
private String getPrivateString(){
return this.privateString;
}
}
PrivateObject privateObject = new PrivateObject("The Private Value");
Method privateStringMethod = PrivateObject.class.
getDeclaredMethod("getPrivateString", null);
privateStringMethod.setAccessible(true);
String returnValue = (String)
privateStringMethod.invoke(privateObject, null);
System.out.println("returnValue = " + returnValue);
Read detail at
http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html
Area s=(Area)c.newInstance();
s.setRadius(10);
System.out.println("Area: "+s.calculateArea(4));
Method m[]=c.getDeclaredMethods();
Constructor c1[]=c.getConstructors();
for(int i=0;i<m.length;i++)
System.out.println(""+m[i]);
for(int i=0;i<c1.length;i++)
System.out.println(""+c1[i]);
Example as below:
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
class Test
{
private int a = 5; // Private data member
private void call(int n) // Private method
{
System.out.println("in call() n: " + n);
}
}
public class Sample
{
public static void main(String args[]) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException
{
Class c = Class.forName("Test");
Object obj = c.newInstance();
//---- Accessing a private method
Method m=c.getDeclaredMethod("call",new Class[]{int.class});
m.setAccessible(true);
m.invoke(obj,7);
//---- Accessing a private data member
Field d = c.getDeclaredField("a");
d.setAccessible(true);
System.out.println(d.getInt(obj));
}
}
To answer your third question:
Is there a way to prevent anyone from accessing private constructors, methods, and data members?
Answer:
Yes, you can restrict the access (you can throw an exception when someone tries to access your private constructor/method/data)
Refer to the below example:
******JavaSingleton Class******
package server;
public class JavaSingleton {
private static final JavaSingleton INSTANCE = new JavaSingleton();
private JavaSingleton() {
if (INSTANCE != null) {
throw new IllegalStateException("Inside JavaSingleton(): JavaSingleton " +
"instance already created.");
}
System.out.println("Inside JavaSingleton(): Singleton instance is being created.");
}
public static final JavaSingleton getInstance() {
return INSTANCE;
}
}
***Listing 2: JavaSingleton client***
import server.JavaSingleton;
import java.lang.reflect.*;
public class TestSingleton {
public static void main(String[] args) throws ReflectiveOperationException {
System.out.println("Inside main(): Getting the singleton instance using getInstance()...");
JavaSingleton s = JavaSingleton.getInstance();
System.out.println("Inside main(): Trying to use reflection to get another instance...");
Class<JavaSingleton> clazz = JavaSingleton.class;
Constructor<JavaSingleton> cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
JavaSingleton s2 = cons.newInstance();
}
}
Output:
C:\singleton>java TestSingleton
Inside main(): Getting the singleton instance using getInstance()...
Inside JavaSingleton(): Singleton instance is being created.
Inside main(): Trying to use reflection to get another instance...
Exception in thread "main" java.lang.reflect.InvocationTargetException
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at TestSingleton.main(TestSingleton.java:13)
Caused by: java.lang.IllegalStateException: Inside JavaSingleton(): JavaSingleton instance already created.
at server.JavaSingleton.<init>(JavaSingleton.java:7)
... 5 more
This example was for a singleton class (checking in the constructor), but you can still implement this logic for the private methods that you want to prevent access from other classes.
In this case you will also declare a static instance and check the value of it in the private method and throw an error in case of any unwanted value.