I have been working with Fields for parsing a class to a SharedPreferences. It worked while the settings class was not a Singleton. I changed to work as a Singleton and it is not working anymore.
G2ASettings settings = G2ASettings.getInstance();
Field[] fields = G2ASettings.class.getDeclaredFields();
for (Field f : fields) {
f.setAccessible(true);
try {
if (preferences.contains(f.getName()) && !f.getName().equals("INSTANCE")) {
f.set(settings, preferences.getBoolean(f.getName(), f.getBoolean(settings))); //si no lo encuentra, pone el valor por defecto determinado en la clase
} else {
if (blablabla) {
editor.putBoolean(f.getName(), true);
allPreferencesDisabled = true;
} else
(*)-----> editor.putBoolean(f.getName(), f.getBoolean(settings));
}
(*)-----> if (!allPreferencesDisabled) allPreferencesDisabled = f.getBoolean(settings);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
The error reported is the following:
Caused by: java.lang.IllegalArgumentException: not a primitive field
The error is repoted at lines marked as (*) in the code.
The fields of G2ASettings are all public and boolean except INSTANCE variable which is G2ASettings type variable and is private.
I will be reporting here my progress.
EXTRA: I post here only relevant data of G2ASettings class (usual Singleton class):
public class G2ASettings implements Serializable {
public boolean {big amount of boolean variables]
private static G2ASettings INSTANCE = null;
private synchronized static void createInstance() {
if (INSTANCE == null) {
INSTANCE = new G2ASettings();
}
}
public static G2ASettings getInstance() {
if (INSTANCE == null) createInstance();
return INSTANCE;
}
{public getters and setters}
}
Which field is the one you're on when it throws this exception? I'd bet money it's INSTANCE, which as you said isn't a Boolean so you can't call getBoolean() on it.
Your if statement that checks if the current field is not INSTANCE before trying to get its Boolean value needs to be earlier and cover all of your other logic, since INSTANCE is never going to have a Boolean value and shouldn't be persisted.
Related
I have upcasted Model2 instance in the following code to Object class and then downcasted back to the class - Model2 in the test method of Model1 class. But thenUov its attribute value after downcasting is showing null. Is this expected for the instance to loose its state after Upcasting-then-Downcasting? Could you please share some details for the same. Also, is there a way to retain the state of the instance after upcasting?
public class Demo{
public static void main(String[] args) {
Model1 m1 = new Model1();
m1.setAttr1(10);
Model2 m2 = new Model2();
m2.setAttr2(10);
m1.test(m2);
}
}
class Model1 {
private Integer attr1;
public Integer getAttr1() {
return attr1;
}
public void setAttr1(Integer attr1) {
this.attr1 = attr1;
}
public void test(Object o) {
Model2 m = (Model2) o;
System.out.println(m.getAttr2());
}
}
class Model2 {
private Integer attr2;
public Integer getAttr2() {
return attr2;
}
public void setAttr2(Integer attr1) {
this.attr2 = attr2;
}
}
No, you don't lose data when casting - an Object is the Object it is.
You are getting null due to a typo in Model2
public void setAttr2(Integer attr1) {
this.attr2 = attr2;
}
You are setting attr2 to itself, the function definition should be (Interger attr2)
Changing that will output 10
Casting doesn't loose any information in Java, it simply changes the level of abstraction (i.e you can't access method getAttr2() on 'o' whilst it is still cast as an object)
The real problem is a typo in model2. You are setting the variable 'attr2' to itself, which is set to null to start with
You want it to be this.attr2 = attr1
I realise that this is probably just a test class, but I would like to also point out that it's good practise to check your argument before casting it - you could have been passed a null or an object of the wrong type.
public void test(Object o) {
if (o == null) {
System.out.println("Don't give me a null");
} else if (o instanceof Model2) {
Model2 m = (Model2) o;
System.out.println(m.getAttr2());
} else {
System.out.println("Wrong type. Was given " + o.getClass());
}
}
In a real class you'd actually do something useful in those sections such as throw an exception
This question seems awkward but we are facing a strange behaviour while retrieving the PropertyDescriptors of a javabean.
Here are the execution results on 1.6, 1.7 and 1.8 of a simple piece of code, compiled with 1.6 compliance.
Java 1.6 execution:
java.beans.PropertyDescriptor#4ddc1428 <- Not important
java.beans.IndexedPropertyDescriptor#7174807e <- Yes I have an indexed property
Java 1.7 execution:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- Not important
java.beans.IndexedPropertyDescriptor[name=values; indexedPropertyType=class java.lang.String; indexedReadMethod=public java.lang.String JavaBean.getValues(int)] <- Yes I have an indexed property
Java 1.8 execution:
java.beans.PropertyDescriptor[name=class; propertyType=class java.lang.Class; readMethod=public final native java.lang.Class java.lang.Object.getClass()] <- Not important
java.beans.PropertyDescriptor[name=values; propertyType=interface java.util.List; readMethod=public java.util.List JavaBean.getValues()] <- Ouch! This is not an indexed property anymore!
Why has it changed?
The javabean specs states about accessing a property with an index. It is not said as mandatory to use an array as the container of the indexed property. Am I wrong?
I read the specs and chapter 8.3.3 talks about Design Patterns for Indexed properties, not the strict rule.
How to make the previous behaviour coming back again without refactoring all the app ? < Old application, lot of code to modify, etc...
Thanks for the answers,
JavaBean class
import java.util.ArrayList;
import java.util.List;
public class JavaBean {
private List<String> values = new ArrayList<String>();
public String getValues(int index) {
return this.values.get(index);
}
public List<String> getValues() {
return this.values;
}
}
Main class
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
public class Test {
public static void main(String[] args) throws IntrospectionException {
PropertyDescriptor[] descs =
Introspector.getBeanInfo(JavaBean.class).getPropertyDescriptors();
for (PropertyDescriptor pd : descs) {
System.out.println(pd);
}
}
}
From JavaBeans 1.01 specification, section 7.2 “Indexed properties”:
A component may also expose an indexed property as a single array value.
Section 8.3 is describing the design patterns which introspection recognizes, in the absence of explicit BeanInfo. Section 8.3.3 is saying that only array properties will trigger automatic recognition of indexed properties.
You're technically correct; it is not mandatory to use an array. But if you don't, the spec says you have to provide your own BeanInfo to expose the property as an indexed property.
So the answer to your question's title is: Yes, Java 1.8 is JavaBean specs compliant.
I'm not sure why List properties were ever supported. Maybe a future JavaBeans specification was going to support them which has since been withdrawn.
As to your final question: I think you'll have to create a BeanInfo class for each class with List properties. I expect you can create a general superclass to make it easier, something like:
public abstract class ListRecognizingBeanInfo
extends SimpleBeanInfo {
private final BeanDescriptor beanDesc;
private final PropertyDescriptor[] propDesc;
protected ListRecognizingBeanInfo(Class<?> beanClass)
throws IntrospectionException {
beanDesc = new BeanDescriptor(beanClass);
List<PropertyDescriptor> desc = new ArrayList<>();
for (Method method : beanClass.getMethods()) {
int modifiers = method.getModifiers();
Class<?> type = method.getReturnType();
if (Modifier.isPublic(modifiers) &&
!Modifier.isStatic(modifiers) &&
!type.equals(Void.TYPE) &&
method.getParameterCount() == 0) {
String name = method.getName();
String remainder;
if (name.startsWith("get")) {
remainder = name.substring(3);
} else if (name.startsWith("is") &&
type.equals(Boolean.TYPE)) {
remainder = name.substring(2);
} else {
continue;
}
if (remainder.isEmpty()) {
continue;
}
String propName = Introspector.decapitalize(remainder);
Method writeMethod = null;
Method possibleWriteMethod =
findMethod(beanClass, "set" + remainder, type);
if (possibleWriteMethod != null &&
possibleWriteMethod.getReturnType().equals(Void.TYPE)) {
writeMethod = possibleWriteMethod;
}
Class<?> componentType = null;
if (type.isArray()) {
componentType = type.getComponentType();
} else {
Type genType = method.getGenericReturnType();
if (genType instanceof ParameterizedType) {
ParameterizedType p = (ParameterizedType) genType;
if (p.getRawType().equals(List.class)) {
Type[] argTypes = p.getActualTypeArguments();
if (argTypes[0] instanceof Class) {
componentType = (Class<?>) argTypes[0];
}
}
}
}
Method indexedReadMethod = null;
Method indexedWriteMethod = null;
if (componentType != null) {
Method possibleReadMethod =
findMethod(beanClass, name, Integer.TYPE);
Class<?> idxType = possibleReadMethod.getReturnType();
if (idxType.equals(componentType)) {
indexedReadMethod = possibleReadMethod;
}
if (writeMethod != null) {
possibleWriteMethod =
findMethod(beanClass, writeMethod.getName(),
Integer.TYPE, componentType);
if (possibleWriteMethod != null &&
possibleWriteMethod.getReturnType().equals(
Void.TYPE)) {
indexedWriteMethod = possibleWriteMethod;
}
}
}
if (indexedReadMethod != null) {
desc.add(new IndexedPropertyDescriptor(propName,
method, writeMethod,
indexedReadMethod, indexedWriteMethod));
} else {
desc.add(new PropertyDescriptor(propName,
method, writeMethod));
}
}
}
propDesc = desc.toArray(new PropertyDescriptor[0]);
}
private static Method findMethod(Class<?> cls,
String name,
Class<?>... paramTypes) {
try {
Method method = cls.getMethod(name, paramTypes);
int modifiers = method.getModifiers();
if (Modifier.isPublic(modifiers) &&
!Modifier.isStatic(modifiers)) {
return method;
}
} catch (NoSuchMethodException e) {
}
return null;
}
#Override
public BeanDescriptor getBeanDescriptor() {
return beanDesc;
}
#Override
public PropertyDescriptor[] getPropertyDescriptors() {
return propDesc;
}
}
I am facing the same issue. I am trying to save StartDate and End date as List from JSP but it is not saved and values are wiped out. In my project, there are start date and end date fields. I debugged BeanUtilsBean then I observed that fields do not have writeMethod. I have added one more setter method for each field in my class and it works.
java.beans.PropertyDescriptor[name=startDateStrings; propertyType=interface java.util.List; readMethod=public java.util.List com.webapp.tradingpartners.TradingPartnerNewForm.getStartDateStrings()]
This is a JavaBeans crapspec problem, which only allows void setters. If you want this-returning setters, then you can't have JavaBeans compatibility and there's nothing Lombok could do about it.
In theory, you could generate two setters, but then you'd have to call them differently and having two setters per field is simply too bad.
<cc:dateInput property='<%= "startDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>
<cc:dateInput property='<%= "endDateStrings[" + row + "]" %>' onchange="setPropertyChangedFlag()"/>
public List<String> getStartDateStrings() {
return startDateStrings;
}
public String getStartDateStrings(int index) {
return startDateStrings.get(index);
}
public void setStartDateStrings(int index, String value) {
startDateStrings.set(index, value);
}
public List<String> getEndDateStrings() {
return endDateStrings;
}
public String getEndDateStrings(int index) {
return endDateStrings.get(index);
}
public void setEndDateStrings(int index, String value) {
endDateStrings.set(index, value);
I'm trying to use a singleton (PhotoStorage) to provide an arrayList of Photo objects, but it seems that the PhotoStorage instance is not behaving as a singleton (two instances).
I am using dagger to inject this singleton into a class named PhotoInteractor. The objectGraph seems A-OK up to this point.
The same PhotoInteractor instance is used in three fragments in a viewpager. These fragments are all instantiated at runtime:
RecentFragment:
HistoryFragment:
Notice how the instance #4067 of the PhotoInteractor is the same for both fragments.
Also:
mAppContext#4093: same
photoStorage#4094: same
When I click a photo object (grid image) from RecentFragment, the PhotoStorage.addPhoto(url) method is called. This correctly adds the photo object to the photoStorage instance array (4094). That much is OK.
Problem:
When I close the applicaton, it is intended that the PhotoStorage.savePhotosToFile method serialzes this arrayList object into JSON on the filesystem.
The following method is called from the same PhotoInteractor instance:
#Override
public void savePhotos(){
photoStorage.get(mAppContext).savePhotosToFile();
}
When I debug the application, the PhotoStorage.get method already has a singleton instance, but what appears to be a 2nd instance!
//Singleton enforcement
public static PhotoStorage get(Context c){
if(sPhotoStorage == null){
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
return sPhotoStorage;
}
This means that the ArrayList of photos will always be empty since it is a new instance of PhotoStorage. I’m not sure where it is instantiating itself from.
Edit - Added PhotoStorage.class:
public class PhotoStorage{
private ArrayList<Photo> mPhotos;
private PhotoJSONer mSerializer;
private static PhotoStorage sPhotoStorage;
private static Context mAppContext;
private static final String PHOTOS_DATABASE = "photos.json";
public static final String TAG = PhotoStorage.class.getSimpleName();
public PhotoStorage(Context appContext){
mSerializer = new PhotoJSONer(appContext, PHOTOS_DATABASE);
try{
mPhotos = mSerializer.loadPhotos();
}catch(Exception e){
mPhotos = new ArrayList<Photo>();
}
}
//Singleton enforcement
public static PhotoStorage get(Context c){
if(sPhotoStorage == null){
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
return sPhotoStorage;
}
public ArrayList<Photo> getPhotos(){
return mPhotos;
}
public Photo getPhoto(String url){
for(Photo p: mPhotos){
if(p.getUrl() == url)
return p;
}
return null;
}
public void deletePhoto(String url){
Log.i(TAG, "deleted photo");
mPhotos.remove(url);
}
public void addPhoto(Photo photo){
Log.i(TAG, "added photo");
mPhotos.add(photo);
}
public boolean savePhotosToFile(){
try{
mSerializer.savePhotos(mPhotos);
return true;
}catch (Exception e){
return false;
}
}
}
You are not executing Singletton pattern in the correct way,
The Singleton design pattern addresses all of these concerns. With the Singleton design pattern you can:
Ensure that only one instance of a class is created
Provide a global point of access to the object
In your case, we don't see PhotoStorage class but this call comes from an instance, what is not allowed by Singletton pattern:
photoStorage.get(mAppContext).savePhotosToFile();
//↑ instance call WRONG!!
This line works, but as your get method is static is not a good practice as Karakuri pointed and also breaks the Singletton pattern definition.
public static PhotoStorage get(Context c){
SOLUTION
To make photoStorage.get() invalid and create a correct Singletton pattern you must:
declare the getInstance() method static in the class (here PhotoStorage)
hide default constructor to avoid instances of the class
create private constructors if necessary
call getInstance() it in a static way:
class PhotoStorage {
// hidding default constructor
private PhotoStorage () {};
// creating your own constructor but private!!!
private PhotoStorage(Context appContext){
mSerializer = new PhotoJSONer(appContext, PHOTOS_DATABASE);
try{
mPhotos = mSerializer.loadPhotos();
}catch(Exception e){
mPhotos = new ArrayList<Photo>();
}
}
//Singleton enforcement
public synchronized static PhotoStorage get(Context c){
if(sPhotoStorage == null){
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
return sPhotoStorage;
}
}
Then you can make static call from everywhere the class scope allows:
#Override
public void savePhotos(){
PhotoStorage.get(mAppContext).savePhotosToFile();
//↑ static call CORRECT!!
}
UPDATE: if your app have several threads and singleton getInstance requests may overlapp, there is a double check syncronized singletton pattern you can apply:
//Singleton enforcement
public synchronized static PhotoStorage get(Context c){
if(sPhotoStorage == null){
synchronized(PhotoStorage.class) {
if(sPhotoStorage == null) {
sPhotoStorage = new PhotoStorage(c.getApplicationContext());
}
}
}
}
I want to get my class property with string name.
I have a code like this
class Test
{
public String simple = "hello";
public void getSetting()
{
try
{
Test c = new Test();
Class cls = this.getClass();
Field field = cls.getField("simple");;
}
catch(Exception e)
{
// error
}
}
}
I get an error with this code , because my property is non-static , and when i changing my property to static , it's work fine , how can i get non-static properties with reflection?
Here's a self-contained example on how to get an instance Field through reflection.
public class Main {
// the instance field
String simple = "foo";
// some static main method
public static void main(String[] args) throws Exception {
// initializing the class as we're accessing an instance method
new Main().reflect();
}
public void reflect() {
Class<?> c = this.getClass();
try {
// using getDeclaredField for package-protected / private fields
Field field = c.getDeclaredField("simple");
// printing out field's value for this instance
System.out.println(field.get(this));
}
// TODO handle better
catch (IllegalAccessException iae) {
iae.printStackTrace();
}
catch (NoSuchFieldException n) {
n.printStackTrace();
}
}
}
Output
foo
try
{
Test c = new Test();
Class cls = c.getClass(); //Change this.getClass to c.getClass()
Field field = cls.getField("simple");;
}
The Field must be static or belong to a instance that can be get via reflection.
Okay, so I have a java file which is loading another class and I want the java file to be able to edit and read variables from the class which is running.
For example:
I have a button which when pressed it sets a variable (This is the class file). I want the java file which is loading this class to be able to see the new value of the variable read it, set it and do whatever is needed. And I want the new value which is set to show up on the running java class.
This is what I have tried so far but when I try to edit the values like getting baseX it doesn't show up on the running class. Also, the baseX value should change when I do stuff on the running class but the stuff is not printed to the screen when I change them. It's as if reflection can't read stuff on runtime. So what does?
Class c = Class.forName("Client");
for (Method m : c.getMethods()) {
if (m.getName().contentEquals("main")) {
Object[] passedArgs = { args };
m.invoke(null, passedArgs);
}
}
Object instance = c.newInstance();
Field baseX = c.getField("baseX");
Field loggedIn = c.getField("loggedIn");
boolean gotValues = false;
while(!gotValues) {
boolean loggedin = loggedIn.getBoolean(instance);
if(loggedin) {
System.out.println(baseX.get(instance));
} else {
System.out.println(loggedin);
loggedIn.setBoolean(instance, true);
}
}
Also yeah getter/setter methods would work if they worked on runtime and I could make it so that when button x is pressed variable y changes on screen. What is a java bean? Also what if I wanted to just invoke a method and not get a value? Or what if I wanted to add my own methods/code?
Try this:
public class Client {
public Object baseX = new Object();
public boolean loggedIn;
}
-----
public class Main {
public static void main(String[] args) throws Exception {
Class c = Class.forName("Client");
/*for (Method m : c.getMethods()) {
if (m.getName().contentEquals("main")) {
Object[] passedArgs = {args};
m.invoke(null, passedArgs);
}
}*/
Object instance = c.newInstance();
Field baseX = c.getField("baseX");
Field loggedIn = c.getField("loggedIn");
boolean gotValues = false;
//while (!gotValues) {
boolean loggedin = loggedIn.getBoolean(instance);
if (loggedin) {
System.out.println("Logged in!");
System.out.println(baseX.get(instance));
}
else {
System.out.println("NOT Logged in!");
System.out.println(loggedin);
loggedIn.setBoolean(instance, true);
System.out.println(loggedIn.getBoolean(instance));
}
//}
}
}