Parcelable and inheritance in Android - java

I got an implementation of Parcelable working for a single class that involves no inheritance. I have problems figuring out the best way to implement the interface when it come to inheritance. Let's say I got this :
public abstract class A {
private int a;
protected A(int a) { this.a = a; }
}
public class B extends A {
private int b;
public B(int a, int b) { super(a); this.b = b; }
}
Question is, which is the recommended way to implement the Parcelable interface for B (in A? in both of them? How?)

Here is my best solution, I would be happy to hear from somebody that had a thought about it.
public abstract class A implements Parcelable {
private int a;
protected A(int a) {
this.a = a;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(a);
}
protected A(Parcel in) {
a = in.readInt();
}
}
public class B extends A {
private int b;
public B(int a, int b) {
super(a);
this.b = b;
}
public static final Parcelable.Creator<B> CREATOR = new Parcelable.Creator<B>() {
public B createFromParcel(Parcel in) {
return new B(in);
}
public B[] newArray(int size) {
return new B[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(b);
}
private B(Parcel in) {
super(in);
b = in.readInt();
}
}

This is my variant. I think it's nice because it shows the symmetry between the virtual read- and write- methods very clearly.
Side note: I think Google did a really poor job at designing the Parcelable interface.
public abstract class A implements Parcelable {
private int a;
protected A(int a) {
this.a = a;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(a);
}
public void readFromParcel(Parcel in) {
a = in.readInt();
}
}
public class B extends A {
private int b;
public B(int a, int b) {
super(a);
this.b = b;
}
public static final Parcelable.Creator<B> CREATOR = new Parcelable.Creator<B>() {
public B createFromParcel(Parcel in) {
return new B(in);
}
public B[] newArray(int size) {
return new B[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeInt(b);
}
public void readFromParcel(Parcel in) {
super(in);
b = in.readInt();
}
}

Here is the implementation for class A in a real world setting since class B will likely have more than one object with different types other than int
It uses reflection to get the types. Then uses a sorting function to sort the fields so that reading and writing happen in the same order.
https://github.com/awadalaa/Android-Global-Parcelable

Related

Intellij - refactor getters and setters using delegate class

Here's what I'd like to do. Let's say I have this code:
public class Foo {
private Bar bar = new Bar();
public void doWork() {
bar.setA(5);
bar.setB(10);
}
}
public class Bar {
private int a;
private int b;
public void setA(int a) { this.a = a; }
public void setB(int b) { this.b = b; }
...
}
I want to extract members a and b from Bar into a separate Container class and end up with this code. Notice that Foo doesn't call setA on bar anymore, instead it requests container and calls a setter on it instead:
public class Foo {
private Bar bar = new Bar();
public void doWork() {
bar.getContainer.setA(5);
bar.getContainer.setB(10);
}
}
public class Bar {
private Container container;
public Container getContainer() { return container; }
...
}
public class Container {
private int a;
private int b;
public void setA(int a) { this.a = a; }
public void setB(int b) { this.b = b; }
...
}
Is there a way to do this in IntelliJ?
I could try using Refactor -> Extract -> Delegate, but in that case IntelliJ leaves setA and setB methods in Bar and doesn't change code in Foo:
public class Bar {
private Container container;
public void setA(int a) { container.setA(a); }
public void setB(int b) { container.setB(b); }
...
}
which is not quite what I want.
Select the piece of code inside class bar...
private int a;
private int b;
public void setA(int a) { this.a = a; }
public void setB(int b) { this.b = b; }
On the main menu, or from the context menu of the selection, choose Refactor | Extract | Method Object . You will also have option to choose to create inner class, or anonymous class. Hope this helps.

Find Java Error

Is there any error in the following Java code? I did not find one when I ran it.
public class WayToGo
{
private int aa, bb;
public void WayToGo(int a, int b)
{
aa = a;
bb = b;
}
}
If this is supposed to be a constructor :
public void WayToGo( int a,int b)
change it to :
public WayToGo( int a,int b)
A constructor has no return type. Not even void.
public class WayToGo {
private int aa, bb;
public WayToGo(int a, int b) {
***this.aa*** = a;
***this.bb*** = b;
}
}

Abstract class as parcelable

Basicly I have the following structure in my app:
It would be straightforward to implement such a structure without the abstract class ProjectItem, but in this case I don't know how to implement this.
The abstract class ProjectItem needs a CREATOR as it should be parcelable. (like
in.readTypedList(mProjectItems, ProjectItem.CREATOR); within the constructor Project(Parcel in))
But in fact, the CREATOR can only be implemented in its derived classes for logical reasons.
So, how to implement this structure in order to keep the class Project parcelable??
Edit
This is what one of the constructors of Project looks like:
private Project(Parcel in) {
in.readTypedList(mProjectItems, ProjectItem.CREATOR);
}
But as I already said, ProjectItem shouldn't have to implement a CREATOR
My solution is similar to evertvandenbruel's. But I identify the concrete class using an int so that I can use a switch block. I also have that switch block in a static getConcreteClass(Parcel) method.
AbstractClass.java
public abstract class AbstractClass implements Parcelable {
public static final int CLASS_TYPE_ONE = 1;
public static final int CLASS_TYPE_TWO = 2;
public static final Creator<AbstractClass> CREATOR = new Creator<AbstractClass>() {
#Override
public AbstractClass createFromParcel(Parcel source) {
return AbstractClass.getConcreteClass(source);
}
#Override
public AbstractClass[] newArray(int size) {
return new AbstractClass[size];
}
};
protected String mAbstractClassString;
public AbstractClass(String abstractClassString) {
mAbstractClassString = abstractClassString;
}
public AbstractClass(Parcel source) {
mAbstractClassString = source.readString();
}
public static AbstractClass getConcreteClass(Parcel source) {
switch (source.readInt()) {
case CLASS_TYPE_ONE:
return new ConcreteClassOne(source);
case CLASS_TYPE_TWO:
return new ConcreteClassTwo(source);
default:
return null;
}
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mAbstractClassString);
}
#Override
public String toString() {
return "Parent String: " + mAbstractClassString + '\n';
}
}
ConcreteClassOne.java
public class ConcreteClassOne extends AbstractClass {
private String mString;
public ConcreteClassOne(String abstractClassMemberString, String string) {
super(abstractClassMemberString);
mString = string;
}
public ConcreteClassOne(Parcel source) {
super(source);
mString = source.readString();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(CLASS_TYPE_ONE);
super.writeToParcel(dest, flags);
dest.writeString(mString);
}
#Override
public String toString() {
return super.toString().concat("Child String: " + mString);
}
}
ConcreteClassTwo.java
public class ConcreteClassTwo extends AbstractClass {
private String mString;
private int mInt;
public ConcreteClassTwo(String abstractClassString, String string, int anInt) {
super(abstractClassString);
mString = string;
mInt = anInt;
}
public ConcreteClassTwo(Parcel source) {
super(source);
mString = source.readString();
mInt = source.readInt();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(CLASS_TYPE_TWO);
super.writeToParcel(dest, flags);
dest.writeString(mString);
dest.writeInt(mInt);
}
#Override
public String toString() {
String string = super.toString();
for (int i = 0; i < mInt; i++) {
string = string.concat("Child String: " + mString + '\n');
}
return string;
}
}
The selected answer (from evertvandenbruel's post) has a bug in it. The correct code must account for parceling when just one of the subclasses is being parceled, not just a list of the superclass objects.
All the other code should be the same, the key is that you MUST read in the type variable in ALL creators (see code below). Otherwise there will be issues with the ordering when trying to unparcel a subclass object
Ex:
package com.example.parcelable_example.model;
import android.os.Parcel;
import android.os.Parcelable;
public class Cat extends Animal{
public Cat(String name){
super(name, "Cat");
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(getType());
super.writeToParcel(dest, flags);
}
public Cat(Parcel source) {
super(source);
}
public static final Parcelable.Creator<Cat> CREATOR = new Parcelable.Creator<Cat>() {
public Cat createFromParcel(Parcel in) {
/** DO NOT FORGET THIS!!! **/
type = in.readString();
return new Cat(in);
}
public Cat[] newArray(int size) {
return new Cat[size];
}
};
}
This question arises from a false assumption.
Here is a quote from the original post.
The abstract class ProjectItem needs a CREATOR as it should be
parcelable.
In fact, It is not necessary for the super class to define CREATOR since it is abstract.
Here is a minimal example which demonstrates the method.
/* Super class */
abstract class SuperClass
implements Parcelable {
protected SuperClass(Parcel in) {
mSuperId = in.readLong();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(mSuperId);
}
}
/* Sub class */
public class SubClass
extends SuperClass {
protected SubClass(Parcel in) {
super(in);
mSubId = in.readLong();
}
#Override
public void writeToParcel(Parcel dest, int flags) {
super.writeToParcel(dest, flags);
dest.writeLong(mSubId);
}
#Override
public int describeContents() {
return 0;
}
public static final Creator<SubClass> CREATOR = new Creator<SubClass>() {
#Override
public SubClass createFromParcel(Parcel in) {
return new SubClass(in);
}
#Override
public SubClass[] newArray(int size) {
return new SubClass[size];
}
};
}
/* Usage */
class AnotherClass {
void aMethod() {
Bundle args = new Bundle();
args.putParcelable("EXTRA_SUPER_CLASS", subClassObject);
}
}
public abstract class A implements Parcelable {
private int a;
protected A(int a) {
this.a = a;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(a);
}
protected A(Parcel in) {
a = in.readInt();
}
}
public class B extends A {
private int b;
public B(int a, int b) {
super(a);
this.b = b;
}
public static final Parcelable.Creator<B> CREATOR = new Parcelable.Creator<B>() {
public B createFromParcel(Parcel in) {
return new B(in);
}
public B[] newArray(int size) {
return new B[size];
}
};
public int describeContents() {
return 0;
}
}

Obfuscate Parcelable classes with proguard

I'm trying to obfuscate a parcelable class with Proguard:
Before adding the Parcelable part the class is:
public class Foo{
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
The obfuscated result is:
public class a
{
private String a;
public String a()
{
return this.a;
}
public void a(String paramString)
{
this.a = paramString;
}
}
After adding implementing parcelable the example class is
public class Foo implements Parcelable {
private String value;
private Foo(Parcel in) {
value = in.readString();
}
public Foo() {
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(value);
}
public static final Parcelable.Creator<Foo> CREATOR
= new Parcelable.Creator<Foo>() {
public Foo createFromParcel(Parcel in) {
return new Foo(in);
}
public Foo[] newArray(int size) {
return new Foo[size];
}
};
}
The obfuscated result is
public class Foo implements Parcelable {
public static final Parcelable.Creator CREATOR = new a();
private String a;
public Foo() {
}
private Foo(Parcel paramParcel) {
this.a = paramParcel.readString();
}
public String a() {
return this.a;
}
public void a(String paramString) {
this.a = paramString;
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel paramParcel, int paramInt) {
paramParcel.writeString(this.a);
}
}
class a implements Parcelable.Creator {
public Foo a(Parcel paramParcel) {
return new Foo(paramParcel, null);
}
public Foo[] a(int paramInt) {
return new Foo[paramInt];
}
}
How can I configure proguard for obfuscate the whole class (including name, params and methods) except the parcelable part?
Thanks
Try putting this in your proguard.cfg file:
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
That should preserve Parcelable part and obfuscate everything else.

Changing a java object outside its class

Here's my question, how can I change an object outside of it's class, so that it maintains the changes made in the outside class?
Here's an example of the code:
Main class:
public class Main {
public static void main(String[] args)
{
Variable var = new Variable(1,2,3);
Change.changeVar(var);
System.out.println("" + var.geta() + "" + var.getb() + "" + var.getc());
}
}
Variable class:
public class Variable {
private int a;
private int b;
private int c;
public Variable(int a, int b, int c)
{
this.a = a;
this.b = b;
this.c = c;
}
public int geta()
{
return this.a;
}
public int getb()
{
return this.b;
}
public int getc()
{
return this.c;
}
}
Change class:
public class Change {
public static void changeVar(Variable var)
{
Variable var2 = new Variable(4,5,6);
var = var2;
}
}
In your example, no. When changeVar() exits, the parameter var is discarded, and the var in your main() method retains its original value. Read up on pass by reference.
public class Variable {
private int a;
private int b;
private int c;
public Variable(int a, int b, int c)
{
this.a = a;
this.b = b;
this.c = c;
}
public int geta()
{
return this.a;
}
public int getb()
{
return this.b;
}
public int getc()
{
return this.c;
}
// depending on your use case, setters might be more appropriate
// it depends on how you want to control the changing of the vars
public void update(int a, int b, int c) {
this.a = a;
this.b = b;
this.c = c;
}
}
public class Change {
public static void changeVar(Variable var)
{
var.update(4,5,6);
}
}
You cannot do it in a way that you described, because in Java variables are passed by values. However you can achieve the desired effect in a different way:
public class Variable {
private int a;
private int b;
private int c;
public Variable(int a, int b, int c)
{
this.a = a;
this.b = b;
this.c = c;
}
public int geta()
{
return this.a;
}
public int getb()
{
return this.b;
}
public int getc()
{
return this.c;
}
public void seta(int a) { this.a = a; }
public void setb(int b) { this.a = b; }
public void setc(int c) { this.a = c; }
}
public class Change {
public static void changeVar(Variable var)
{
var.seta(4);
var.setb(5);
var.setc(6);
}
}
You need to provide setter methods and call them on the original object:
public void seta(int newa) { this.a = newa; }
Then you would say
public static void changeVar(Variable var)
{
var.seta(4);
//etc
}
You are merely repointing the local variable reference var to point to your new instance var2. It has no effect on the value of the original instance passed into the method.
Doing it that way? You can't.
You're passing a reference to the instance. However, inside the function, you use a new reference. Assigning to the new reference does not affect others.
public static void changeVar(Variable var)
{
Variable var2 = new Variable(4,5,6);
var = var2;
}
first, u can write some setter methods in Variable class, then you can call these setter methods in the above code, like var.setA(4) ... and so on.enter code here

Categories