I'm trying to send a list of objects over a socket.
The objects in the list contain an unserializable object and so cannot be sent, however it's base class is fully serializable and contains all the fields I need.
So what I'm trying to do is convert the list to a list of the base class. The only way I could think so do this is as follows:
// subClassList is an ArrayList<SubClass>
ArrayList<BaseClass> baseClassList = new ArrayList<BaseClass>();
for(SubClass subClass: subClassList) {
// cast to the base class
baseClassList.add((BaseClass)subClass);
}
This however doesn't work as I still get the same NotSerializableException exception. From debugging the code I can see that the new list is still a list of the sub class, even though it has been cast.
Is there a way to achieve what I'm trying to do?
Casting a reference doesn't change the type of the actual object:
String foo = "hello";
Object bar = (Object) foo;
System.out.println(bar); // Hello
System.out.println(bar.getClass()); // java.lang.String
If you want to only have an instance of the base class, you could create a constructor in the base class which simply creates a new instance of the base class given an instance of it, without trying to perform any sort of deep copy:
public BaseClass(BaseClass other) {
this.x = other.x;
this.y = other.y;
this.z = other.z;
// etc
}
Then in the code you've shown:
baseClass.add(new BaseClass(subClass));
Your approach doesn't work because even though you can use a super class variable to refer to the subclass object, the object itself is of the sub class type.
The best way is to either serialize the subclass, or extract the data from subclass and write them to a newly created super class. Just have a static utility method to do the conversion build into the sub class....
Casting doesn't change the type of an object. It allows getting a reference of another type for the object iff the object has this other type. Upcasting is never necessary. You can use
String s = "hello";
Object o = s; // no cast needed
Downcasting is necessary, and will only work if the object has the type you cast it to:
Object o = "hello";
Object o2 = Integer.valueOf(5);
String s = (String) o; // OK because o is a String
String s2 = (String) o2; // ClassCastException, because o2 is not a String.
So, your code is equivalent to
ArrayList<BaseClass> baseClassList = new ArrayList<BaseClass>();
for(SubClass subClass: subClassList) {
// cast to the base class
baseClassList.add(subClass);
}
You must create a new object of the new type, using a copy constructor:
ArrayList<BaseClass> baseClassList = new ArrayList<BaseClass>();
for(SubClass subClass: subClassList) {
// cast to the base class
baseClassList.add(new BaseClass(subClass));
}
Your question embodies a contradiction in terms. If the base class is Serializable so are all its derived classes. If your derived class contains references to non-serializable objects, just make them transient.
Related
I was preparing for Java Certification Exam and there are few scenarios which gets very complicated sometimes when to comes to a mixture of polymorphism, Inheritance, overloading, overriding, Generics as well as Casting.
I am stuck with understanding these examples mentioned below :
// Tree is the base class
class Tree {
String type = "unknown";
String getTreeString() {
return "Tree";
}
}
// DeciduousTree is a subclass of Tree
class DeciduousTree extends Tree {
String type = "deciduous";
#Override
String getTreeString() {
return "Leafy Tree";
}
}
// FruitTree is a subclass of Tree
class FruitTree extends Tree {
String type = "fruit";
#Override
String getTreeString() {
return "Fruit Tree";
}
}
public class UpcastExamples {
public static void main(String[] args) {
UpcastExamples upex = new UpcastExamples();
// Create two specific trees
Tree tree = new Tree();
Tree mapleTree = new DeciduousTree();
Tree appleTree = new FruitTree();
// we upcast deciduousTree to its parent class
Tree genericTreeMaple = (Tree) mapleTree;
Tree genericTreeApple = (Tree) appleTree;
Tree genericTreeTree = (Tree) tree;
// Print mapleTree's type
System.out.println("Tree type = " + mapleTree.type);
// Let's upcast to use the generic Tree's type..
System.out.println("Tree type = " + (genericTreeMaple.type));
// Print Fruit Tree's type
System.out.println("Tree type = " + appleTree.type);
// Upcasting to pass object as a parameter
upex.printTreeType(genericTreeTree);
upex.printTreeType(genericTreeMaple);
upex.printTreeType(genericTreeApple);
upex.printTreeType(tree);
upex.printTreeType(mapleTree);
upex.printTreeType(appleTree);
}
public void printTreeType(Tree tree) {
System.out.println("Tree type = " + tree.getTreeString());
}
}
I expected these upex.printTreeType(genericTreeMaple);upex.printTreeType(genericTreeApple); to print Tree since they are upcasted to Tree(Base Class) but somehow it grabs the methods of its child classes (I know they are overriden) but the same thing when implemented via method overloading then
Animal genericDog = new Dog();Animal genericCat = new Cat(); will hold on to the method of the parent's class.
I tried to remember like this parentClass p = new childClass() will always refer methods of parentClass but it seems to fail during overriding and upcasting.
Also, there are few other things mentioned below that I could not understand, I don't just want to memorize stuffs.
Here BaseClass class is the parent class
And NextClass inherits the BaseClass
BaseClass[] myNextArray = new NextClass[6];
BaseClass[] myNextArray2 = new BaseClass[6];
//Allows this:
NextClass[] nextArray = (NextClass[]) myNextArray; //Line 1
//But doesn't allows this:
NextClass[] nextArray2 = (NextClass[]) myNextArray2;
Do I have to memorize that Array of SubClass is not equal to Array of SuperClass in Java but how come it allows Line 1 is also a big doubt
In Java, each object (which includes arrays) has a type that is determined upon construction, e.g. using the new operator. This type never changes.
Variables only contain references to objects. Think of a remote control. You can refer to an object using a variable having a broader type, i.e. the type of a superclass or interface of the object. But this doesn’t change the type of the object itself.
Therefore, when you invoke an overridable method, you will always invoke the most specific method of the object’s actual type. Further, a type cast will succeed if the object’s actual type is compatible. The variable’s reference type does not tell whether the type cast will succeed, as otherwise, we wouldn’t need the runtime check at all¹.
When you initialize a variable like
BaseClass[] myNextArray = new NextClass[6];
the object’s actual type is NextClass[], hence, a subsequent type cast to NextClass[] can succeed. In contrast, using
BaseClass[] myNextArray2 = new BaseClass[6];
the object’s actual type is BaseClass[], hence, a type cast to NextClass[] will fail.
Note that it is possible to change a reference variable to let it point to a different object, so whether casting the reference to a specific type will succeed may change too.
BaseClass[] myNextArray = new NextClass[6];
NextClass[] nextArray = (NextClass[]) myNextArray; // succeeds
myNextArray = new BaseClass[6]; // let myNextArray refer to a different object
nextArray = (NextClass[]) myNextArray; // will fail
¹ The only exception to the rule is that the compiler will reject certain type casts that can be proven at compile-time to be impossible to succeed, like trying to cast an Integer to a String.
Though my question seems repetition, but I am new to Reflections and could find solution to the exact problem.
I need to write a method, which any class can call to populate its data. For simplicity, I created a class say MappingHelper, with a Factory like method 'Create' which will create its own instance. I need to then populate this instance and return it.
public final Class MappingHelper{
public final Object getBENodeData(Class<?> classRef, String className){
Class myClass = Class.forName(classRef.getName());
Method method = classRef.getMethod("Create",(Class<?>[])null);
Object obj = method.invoke(null, (Object[]) null);
}
}
I need to typecast obj to same type as of 'classRef' so that I can call its instance methods.
Could someone help?
What you're trying to do is not possible with reflection and with your current setup. Even if you manage to cast the object to classRef, you wouldn't know what instance methods to call since getBENodeData presumably can take any type.
What you can do is call the method from a location where the type is known, and cast to it.
Object obj = getBENodeData(MyType1.class, MyType1.class.getName());
MyType1 myType1 = (MyType1) obj;
myType1.setId(..);
Object obj2 = getBENodeData(MyType2.class, MyType2.class.getName());
MyType2 myType2 = (MyType2) obj2;
myType2.setName(..);
Is there a work around that will allow me to cast an object of the base class to an object of the derived class?
something like the following
B extends A
A a = new A();
B b = (B)a
Is there a trick that will achieve this?
No, absolutely not. What would you expect the values of any fields declared in B but not in A to be? For example, what would you expect this to do:
Object x = new Object();
String text = (String) x;
System.out.println(text);
An Object has no text data... so what would it mean to cast it as a string?
You can only cast a reference to a type which is appropriate for the actual type of the object.
The desire to do this usually indicates a design problem somewhere... or it might mean that you want something like:
public class A {
public A() {
// Whatever
}
public A(A a) {
// Use the existing values in "a" to initialize this object
}
}
public class B extends A {
/** Creates a new B from the values in an A, with suitable defaults. */
public B(A a) {
super(a);
// Now initialize any fields in B with appropriate values
}
}
Then:
A a = new A();
B b = new B(a);
That will create two objects, unlike a cast... but it would at least be valid.
How is that even possible? Think about it. It is like saying if you have a class FourWheeler, you can simply cast it into a Ferrari and make it a Ferrari!
No, this isn't possible. When B extends A it inherits the behavior of A, but on the same time, there is nothing stopping you from defining new behavior for B (where those new behaviors won't be part of A)
For example say A has a single method called 'methodA'. Now when B extends A it inherits 'methodA' but it also declares another method called 'methodB'. So under such circumstance you will get a runtime 'ClassCastException' when you try to call the 'methodB' over an instance of Object A.
Say I have two classes where SubClass inherits from (extends) SuperClass.
What is the difference between:
SuperClass obj1 = new SubClass();
and:
SubClass obj2 = new SubClass();
Both will look to the constructor of the Superclass and initialise it (the correct one, obviously). Both have access to the superclass implementations. But one is (as far as I am aware) a subclass wrapped in a superclass (the first example) and the other is a subclass object (the second example).
How will this effect the way code is run and interpreted?
What are the real world issues that a developer needs to be aware of regarding how these two objects differ?
Thanks in advance for any help!
The only difference with initializing it as a superclass is that if the subclass implementation has methods which the superclass does not, they will not be accessible via this object reference.
But "internally", this is still an instance of the subclass; so, if you call a method defined in the superclass but the subclass has overriden it, it is the subclass' method which is called: the JVM looks up methods from the more specific to the more general.
As a convoluted example, let us take Object and String:
final Object o = "Hello!"; // in fact this calls new String("Hello!")
o.toString(); // <-- uses String's .toString(), not Object's
// Can't do that: String defines .subString() but Object does not
o.subString(1);
It may help to think what the compiler knows about and what the runtime knows and a simple example:
public class Product {
public double getPrice() {...}
}
public class Book extends Product() {
public int getPageCount() {...}
}
and a simple program:
Product p = new Product();
p.getPrice(); // OK
p.getPageCount(); // compiler error
Book b = new Book();
b.getPrice(); // OK
b.getPageCount(); // OK
Product pb = new Book();
pb.getPrice(); // OK
pb.getPageCount(); // compiler error
// but we can still use the getPageCount() of pb;
((Book)pb).getPageCount(); // compiles
// however if pb was not a Book then you would get a runtime error (ClassCastException)
You can test for the actual class by:
if (pb instanceof Book) {
((Book)pb).getPageCount();
}
This is often necessary when developing classes, but if your code has a lot of instanceof it probably needs rethinking.
SuperClass obj1 = new SubClass();
What you're seeing here is a type of substitutability. What this means is, let's say you make another sub class of SuperClass, SiblingClass. Without using the superclass reference type, we would have to change more code, getters and setters, collections that might want to use it. By referencing them as the supertype, all you need to do is pass in the new SiblingClass object on construction.
I was wondering whether it is possible to declare a new object of some given type in Java, given that I have that type represented as a Class object.
For instance, let us say that I have
SomeClass obj1;
Class c = obj1.getClass();
Now I would like to take "c" and use it to declare a new object of that type. Something along these lines:
Class<c> my_new_var;
such that my_new_var would then be a variable of same type/class as obj1. This is directly related, I guess, to whether we can use a Class object (or something related to that Class object) as a type in the declaration of a new variable.
Is that possible, or impossible since Java is strongly-typed?
Thanks in advance,
Bruno
YourType newObject = c.newInstance();
But you need to have a no-arg constructor. Otherwise you'd have to do more reflection.
Note that there is no need to cast if your Class is parameterized. If it is not, as in your code, you'd need a cast.
To your 2nd question - no, it is not possible to declare a variable of a dynamic type, because yes, java is statically typed. You must define the type of a variable at compile time. In the above example you can:
use Object, if you don't know the type, because it is the superclass of all objects.
use generics. For example (with exception handling omitted):
public static <T> T createNewInstance(T exampleInstance) {
return (T) exampleInstance.getClass().newInstance();
}
If you have a default constructor:
SomeClass obj2 = (SomeClass) c.newInstance();
Like this:
Class c = Class.forName("com.xyzws.SomeClass");
Object a = c.newInstance();
my_new_var would be of type Class<SomeClass> , which is not the same as obj1 which is of type SomeClass.
However my_new_var.newInstance() does give you a new object which is of the same type as obj1.
Well, you could do this:
SomeClass obj1 = ...
Class<? extends SomeClass> c = obj1.getClass();
SomeClass obj2 = c.newInstance();
This requires a no-arg constructor on whatever subclass (if any) of SomeClass obj1 actually is.
However, this only works because you know the type of the Class object in question. Given some arbitrary raw Class or Class<?> object, you would not know what type of object newInstance() would return and you would only be able to declare that instance as an Object.
Yeap, try:
That a = c.newInstance();
for me this worked:
Object innerObj = classObj.getClass().newInstance();