How can I "force" an upcast? Convert subclass into superclass?

T

Tobias K.

Maybe this is an FAQ, but nevertheless I don't get the point.

Look at this:

public class CastTest {

class ClassA {}
class ClassB extends ClassA {}

CastTest() {
ClassA a = new ClassA();
ClassB b = new ClassB();
ClassA c = (ClassA) b;
System.out.println(a.getClass());
System.out.println(b.getClass());
System.out.println(c.getClass());
}

public static void main(String[] args) {
new CastTest();
}
}

If you execute the obove, you'll get the following:

class test.CastTest$ClassA
class test.CastTest$ClassB
class test.CastTest$ClassB

Why is the class of c still ClassB and _not_ ClassA? I casted it!
Is there a way to force the cast from ClassB to ClassA or do I have to
do it by hand - say: Do I have to write a method that takes all members
that are both present in A and in B out of B, then creates a new A and
puts them into it? Or is there another way?

Thanks in advance!

Tobias
 
H

Hal Rosser

Tobias K. said:
Maybe this is an FAQ, but nevertheless I don't get the point.

Look at this:

public class CastTest {

class ClassA {}
class ClassB extends ClassA {}

CastTest() {
ClassA a = new ClassA();
ClassB b = new ClassB();
ClassA c = (ClassA) b;
System.out.println(a.getClass());
System.out.println(b.getClass());
System.out.println(c.getClass());
}

public static void main(String[] args) {
new CastTest();
}
}

If you execute the obove, you'll get the following:

class test.CastTest$ClassA
class test.CastTest$ClassB
class test.CastTest$ClassB

Why is the class of c still ClassB and _not_ ClassA? I casted it!
Is there a way to force the cast from ClassB to ClassA or do I have to
do it by hand - say: Do I have to write a method that takes all members
that are both present in A and in B out of B, then creates a new A and
puts them into it? Or is there another way?

Thanks in advance!

Tobias

You don't have to cast a subclass for it to be a superclass.
Try the instanceOf operator.
 
M

Matt Humphrey

Tobias K. said:
Maybe this is an FAQ, but nevertheless I don't get the point.

Look at this:

public class CastTest {

class ClassA {}
class ClassB extends ClassA {}

CastTest() {
ClassA a = new ClassA();
ClassB b = new ClassB();
ClassA c = (ClassA) b;
System.out.println(a.getClass());
System.out.println(b.getClass());
System.out.println(c.getClass());
}

public static void main(String[] args) {
new CastTest();
}
}

If you execute the obove, you'll get the following:

class test.CastTest$ClassA
class test.CastTest$ClassB
class test.CastTest$ClassB

Why is the class of c still ClassB and _not_ ClassA? I casted it!
Is there a way to force the cast from ClassB to ClassA or do I have to
do it by hand - say: Do I have to write a method that takes all members
that are both present in A and in B out of B, then creates a new A and
puts them into it? Or is there another way?

It's because you have some misconceptions about what casting is. The new ()
creates an object of the specific class. An object cannot change its
class--the class is an intrinsic part of the object itself. Casting cannot
change the class of the object, only how the compiler treats the reference
at that point. Furthermore, all subclasses *are* members of their
superclasses. All ClassB objects are also ClassA. You don't have to cast a
ClassB object to be able to treat it like a ClassA object.

Cheers,
Matt Humphrey (e-mail address removed) http://www.iviz.com/
 
T

Tobias K.

Matt said:
You don't have to cast a
ClassB object to be able to treat it like a ClassA object.

Well. I've got a method that compares "a" and "b", checks if
they are equal. I thought, "casting" b to a would get rid of the
supplemental members. I tought, if the members being left over were
equal, then a and b would be considered as equal as well...

Maybe I need to define another equal-method, or simply create a new
object of ClassA out of b. The latter is working.

I don't know if my equal-method could be improved?

Have a look:

public class CastTest {

class ClassA {
int fix=100;
public int hashCode() {
return 0;
}
public boolean equals(Object obj) {
if( (obj != null) &&
(obj.getClass().equals(this.getClass())) ) {
ClassA otherObj = (ClassA) obj;
return (this.fix == otherObj.fix);
}
return false;
}
}
class ClassB extends ClassA {
int fix=100;
int supplement=200;
public int hashCode() {
return 1;
}
public boolean equals(Object obj) {
if( (obj != null) &&
(obj.getClass().equals(this.getClass())) ) {
if( super.equals(obj) == false ) {
return false;
}
ClassB otherObj = (ClassB) obj;
return (this.supplement == otherObj.supplement);
}
return false;
}
}

ClassA methodA() {
ClassA a = new ClassA();
a.fix=500;
return a;
}

ClassB methodB() {
ClassB b = new ClassB();
b.fix=500;
return b;
}

boolean methodACompareB(ClassA a, ClassA b) {
return a.equals(b);
}

boolean methodBCompareA(ClassA a, ClassA b) {
return b.equals(a);
}

CastTest() {
ClassA a = new ClassA();
ClassB b = new ClassB();
ClassA c = (ClassA) b;
System.out.println(a.getClass());
System.out.println(b.getClass());
System.out.println(c.getClass());

if (b instanceof ClassA) {
ClassA d = (ClassA) b;
System.out.println(d.getClass());
}

System.out.println(
methodACompareB(methodA(),methodB())
);
System.out.println(
methodBCompareA(methodB(),methodA())
);

a.fix=500;
b.fix=500;
ClassA newB = new ClassA();
newB.fix = b.fix;

System.out.println(
methodACompareB(a,newB)
);
System.out.println(
methodBCompareA(a,newB)
);

}

public static void main(String[] args) {
new CastTest();
}
}

Execute the obove and you'll get:
class test.CastTest$ClassA
class test.CastTest$ClassB
class test.CastTest$ClassB
class test.CastTest$ClassB
false
false
true
true
 
C

Chris Smith

Tobias K. said:
Well. I've got a method that compares "a" and "b", checks if
they are equal. I thought, "casting" b to a would get rid of the
supplemental members.

Nope, sorry. All members of an object will remain members of that
object for as long as the object lives. What casting does is ensure
that one specific *reference* to the object is treated as a different
type, so that even though those members still exist you cannot access
them through that reference.
I don't know if my equal-method could be improved?

You need to define what it means for two objects of these classes to be
equal. Java programmers often develop the unfortunate habit of
mechanically writing equals methods that just compare all the fields of
two objects and returning true or false. Recall that when you declare
that two objects are equal, you're defining that policy for the entire
class hierarchy rooted at the class you're writing. Also keep in mind
the contract requirements posed in the API documentation for Object's
equals method.

Specific comments:
class ClassA {
public boolean equals(Object obj) {
if( (obj != null) &&
(obj.getClass().equals(this.getClass())) ) {
ClassA otherObj = (ClassA) obj;
return (this.fix == otherObj.fix);
}
return false;
}
class ClassB extends ClassA {
public boolean equals(Object obj) {
if( (obj != null) &&
(obj.getClass().equals(this.getClass())) ) {
if( super.equals(obj) == false ) {
return false;
}
ClassB otherObj = (ClassB) obj;
return (this.supplement == otherObj.supplement);
}
return false;
}

This is going the wrong direction. Implementations of equals should
become more permissive toward the leaves of the inheritance hierarchy.
Yours are becoming less permissive. That is, there are less
requirements for objects of class A to be equal to each other than for
objects of class B. When you implement A, think of it as a contract for
*all* instances of A, including those of its subclass. Hence, the first
equals method says that if the classes of objects are the same and
'fix' is equal, then the objects are equal. The implementation in B
should, AT A MINIMUM, at least report objects as equal if they are of
the same class and their 'fix' members are equal. B's implementation
may report more objects as equal than that, but it should NOT report
less.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
A

Andrea Desole

There has been a discussion already a while ago about the equal method,
and some people disagreed with the way I look at this issue.
I personally think that the original method is correct, and since a
class B is a specialization of A it makes sense to think that it's more
restrictive than A. So a B class should use A's conditions and something
else. Otherwise there can be a class C that is also derived from A, but
it's different from B. Still, equals might return true.
 
T

Tony Morris

This is going the wrong direction. Implementations of equals should
become more permissive toward the leaves of the inheritance hierarchy.

Implementations of equals should generally return false if the class of the
Object is not the same as the enclosing class - there are exceptions to this
rule.
This is because failure to do so, almost certainly violates the general
contract of the equals method - specifically, symmetry.
 
C

Chris Smith

Please don't top-post.

Andrea Desole said:
There has been a discussion already a while ago about the equal method,
and some people disagreed with the way I look at this issue.

I'm sure there have been any number of discussions of the equals method
before.
I personally think that the original method is correct, and since a
class B is a specialization of A it makes sense to think that it's more
restrictive than A. So a B class should use A's conditions and something
else. Otherwise there can be a class C that is also derived from A, but
it's different from B. Still, equals might return true.

Do you also apply this standard to the implementation of equals in
java.lang.Object?

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
J

John C. Bollinger

Tony said:
Implementations of equals should generally return false if the class of the
Object is not the same as the enclosing class - there are exceptions to this
rule.
This is because failure to do so, almost certainly violates the general
contract of the equals method - specifically, symmetry.

Only when subclasses further override equals(). Chris Uppal and I may
not completely agree about equals(), but in the recent thread about
equals() he certainly did persuade me that equals() is fundamentally a
very odd method, and certainly a Java wart from an OO point of view.
Every nontrivial override of an equals() method at any point in the
class hierarchy violates the Liskov substitution principle, for
instance.[*] More troubling, perhaps, a class must inherit or define
exactly one equality policy for all contexts. A generic equality test
doesn't make much sense -- there is no special reason why it should even
be possible to ask the question of whether an instance of random class A
is equal to an instance of random class B.


[*] Demonstration of the violation of Liskov:

(a) Original formulation of the Liskov Substitution Principle: "If for
each object o1 of type S there is an object o2 of type T such that for
all programs P defined in terms of T, the behavior of P is unchanged
when o1 is substituted for o2 then S is a subtype of T."

(b) Take type T as java.lang.Object

(c) Take type S as:
public class S {
int i;
public S(int ii) {
this.i = ii;
}
public boolean equals(Object o) {
if (o.getClass() == this.getClass()) {
return (((S) o).i == this.i);
} else {
return false;
}
}
}

(d) Take the class of programs, P, characterized by integer
PROGRAM_PARAMETER and the following Java source code:

public class EqualityTester {
public boolean areObjectsEqual(Object a, Object b) {
return a.equals(b);
}
public static void main(String[] args) {
Object o2 = new Object();
Object obj = new S(PROGRAM_PARAMETER);

System.out.println(areObjectsEqual(o2, obj)
? "Equal" : "Not Equal");
}
}

All of those programs print "Not Equal".

(e) Choose, then, any instance, o1, of class S, and substitute it for o2
in all the programs P. There will be one of those programs that then
prints "Equal" instead of "Not Equal" -- i.e. the behavior of one of the
programs changes. Note that this is independent of the choice of object
o1, so long as that object's class is java.lang.Object. If Liskov were
obeyed then that would imply that S is not a subclass of Object, but
that is incorrect, hence Liskov is not obeyed.

A similar demonstration could be provided for any alternative nontrivial
override of equals() anywhere up and down the class hierarchy. The
demonstration could be formalized into a rigorous general proof, but I
think that's unnecessary for this audience.



John Bollinger
(e-mail address removed)
 
A

Andrea Desole

Do you also apply this standard to the implementation of equals in
java.lang.Object?

no, you are right, this is a good point.
But, given one base class B and two derived classes D1 and D2, wouldn't
you say that generally D1.equals(D2) if:
- their B part is equal
- some extra conditions, related to their D part, is true.
 
C

Chris Smith

Andrea Desole said:
no, you are right, this is a good point.
But, given one base class B and two derived classes D1 and D2, wouldn't
you say that generally D1.equals(D2) if:
- their B part is equal
- some extra conditions, related to their D part, is true.

In general, I'd say no. In fact, it's exactly this mechanical and
habitual implementation of equals that I think Java developers need to
be broken of.

The vast majority of the time -- for example, any time the class is
mutable -- the right thing to do is not to override the equals method at
all. In those few remaining cases where it is reasonable to override
equals, some thought needs to go into the question of what it means for
two classes to be equal. If the class is non-final (another thing that
should be more rare than it is), then the thought needs to consider the
existence of subclasses. The ideal solution is that equality is defined
at only one extra place besides java.lang.Object, and that definition
should be broader than the one in java.lang.Object. If a further
subclass overrides equals again, then it needs to be yet broader than
the next higher implementation.

Anything else exhibits some very counterintuitive behavior.

--
www.designacourse.com
The Easiest Way To Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
C

chairam

public class CastTest {
class ClassA {}
class ClassB extends ClassA {}

CastTest() {
ClassA a = new ClassA();
ClassB b = new ClassB();
ClassA c = (ClassA) b;
System.out.println(a.getClass());
System.out.println(b.getClass());
System.out.println(c.getClass());
}

public static void main(String[] args) {
new CastTest();
}
}

If you execute the obove, you'll get the following:

class test.CastTest$ClassA
class test.CastTest$ClassB
class test.CastTest$ClassB

Why is the class of c still ClassB and _not_ ClassA? I casted it!
[cut]

actually you don't cast b
you have only 2 objects in memory:
one referred by a and one referred by b and c

so it's obvious that: System.out.println(c.getClass());
print: class test.CastTest$ClassB
because you wrote something like:
System.out.println(new ClassB());

Bye
Chairam
 
M

Mike Schilling

John C. Bollinger said:
(a) Original formulation of the Liskov Substitution Principle: "If for
each object o1 of type S there is an object o2 of type T such that for all
programs P defined in terms of T, the behavior of P is unchanged when o1
is substituted for o2 then S is a subtype of T."

(b) Take type T as java.lang.Object

(c) Take type S as:
public class S {
int i;
public S(int ii) {
this.i = ii;
}
public boolean equals(Object o) {
if (o.getClass() == this.getClass()) {
return (((S) o).i == this.i);
} else {
return false;
}
}
}

(d) Take the class of programs, P, characterized by integer
PROGRAM_PARAMETER and the following Java source code:

public class EqualityTester {
public boolean areObjectsEqual(Object a, Object b) {
return a.equals(b);
}
public static void main(String[] args) {
Object o2 = new Object();
Object obj = new S(PROGRAM_PARAMETER);

System.out.println(areObjectsEqual(o2, obj)
? "Equal" : "Not Equal");
}
}

All of those programs print "Not Equal".

(e) Choose, then, any instance, o1, of class S, and substitute it for o2
in all the programs P. There will be one of those programs that then
prints "Equal" instead of "Not Equal" -- i.e. the behavior of one of the
programs changes. Note that this is independent of the choice of object
o1, so long as that object's class is java.lang.Object. If Liskov were
obeyed then that would imply that S is not a subclass of Object, but that
is incorrect, hence Liskov is not obeyed.

Perhaps I'm missing something, but I don't see that overriding Equals is
necessary for this kind of example. Change the example program in (d) to
the following:

public class EqualityTester {
public boolean areObjectsEqual(Object a, Object b) {
return a.equals(b);
}
public static void main(String[] args) {
Object o2 = new Object();
Object obj = new S(PROGRAM_PARAMETER);

System.out.println(areObjectsEqual(o2.getClass(), obj.getClass())
? "Equal" : "Not Equal");
}
}

All will print "Not Equal". Now, as before, choose, then, any instance, o1,
of class S, and substitute it for o2
in all the programs P. All will now print "Equal". This would continue to
be true if S didn't override equals().

I'm tempted to go on to discuss the invariant:

a != b -> !a.equals(b)

This is true for objects of type Object(), but overriding equals() can
violate it. Since I'm not sure if I'm missing your point entirely, I'll not
do that now.
 
A

Andrea Desole

In general, I'd say no. In fact, it's exactly this mechanical and
habitual implementation of equals that I think Java developers need to
be broken of.

The vast majority of the time -- for example, any time the class is
mutable -- the right thing to do is not to override the equals method at
all. In those few remaining cases where it is reasonable to override
equals, some thought needs to go into the question of what it means for
two classes to be equal. If the class is non-final (another thing that
should be more rare than it is), then the thought needs to consider the
existence of subclasses. The ideal solution is that equality is defined
at only one extra place besides java.lang.Object, and that definition
should be broader than the one in java.lang.Object. If a further
subclass overrides equals again, then it needs to be yet broader than
the next higher implementation.

Anything else exhibits some very counterintuitive behavior.

Would you find a more specialized class with a less specialized equals
intutive?
Personally, I'm starting to think that the equals implemented in Object
is not really correct. Actually, it probably doesn't make much sense at
all to consider comparing two objects, since we are comparing two empty
entities we know nothing about.
I would agree on the fact that the equality has to be defined depending
on the case, which might make the entire discussion just quite academic,
since everything or almost everything makes sense. I would still say
that I disagree on the general case, and we might maybe discuss some
examples, but there is something that, for me, plays an important role here.
The JDK specifies the following about equals (this is the documentation
for Object.equals in the JDK 1.4.2):

# It is reflexive: for any non-null reference value x, x.equals(x)
should return true.
# It is symmetric: for any non-null reference values x and y,
x.equals(y) should return true if and only if y.equals(x) returns true.
# It is transitive: for any non-null reference values x, y, and z, if
x.equals(y) returns true and y.equals(z) returns true, then x.equals(z)
should return true.
# It is consistent: for any non-null reference values x and y, multiple
invocations of x.equals(y) consistently return true or consistently
return false, provided no information used in equals comparisons on the
objects is modified.
# For any non-null reference value x, x.equals(null) should return false.

The documentation also says that "equal objects must have equal hash codes"

So, before finding an equals that actually expresses equality according
to our rules, we should find an equals that expresses equality according
to these rules, because they define the semantics of the method.
This is also, by the way, why I might argue that using getClass instead
of instanceof actually respects Liskov, as discussed in another thread
 
J

John C. Bollinger

Mike said:
(a) Original formulation of the Liskov Substitution Principle: "If for
each object o1 of type S there is an object o2 of type T such that for all
programs P defined in terms of T, the behavior of P is unchanged when o1
is substituted for o2 then S is a subtype of T."

(b) Take type T as java.lang.Object

(c) Take type S as:
public class S {
int i;
public S(int ii) {
this.i = ii;
}
public boolean equals(Object o) {
if (o.getClass() == this.getClass()) {
return (((S) o).i == this.i);
} else {
return false;
}
}
}

(d) Take the class of programs, P, characterized by integer
PROGRAM_PARAMETER and the following Java source code:

public class EqualityTester {
public boolean areObjectsEqual(Object a, Object b) {
return a.equals(b);
}
public static void main(String[] args) {
Object o2 = new Object();
Object obj = new S(PROGRAM_PARAMETER);

System.out.println(areObjectsEqual(o2, obj)
? "Equal" : "Not Equal");
}
}

All of those programs print "Not Equal".

(e) Choose, then, any instance, o1, of class S, and substitute it for o2
in all the programs P. There will be one of those programs that then
prints "Equal" instead of "Not Equal" -- i.e. the behavior of one of the
programs changes. Note that this is independent of the choice of object
o1, so long as that object's class is java.lang.Object. If Liskov were
obeyed then that would imply that S is not a subclass of Object, but that
is incorrect, hence Liskov is not obeyed.


Perhaps I'm missing something, but I don't see that overriding Equals is
necessary for this kind of example. Change the example program in (d) to
the following:

public class EqualityTester {
public boolean areObjectsEqual(Object a, Object b) {
return a.equals(b);
}
public static void main(String[] args) {
Object o2 = new Object();
Object obj = new S(PROGRAM_PARAMETER);

System.out.println(areObjectsEqual(o2.getClass(), obj.getClass())
? "Equal" : "Not Equal");
}
}

All will print "Not Equal". Now, as before, choose, then, any instance, o1,
of class S, and substitute it for o2
in all the programs P. All will now print "Equal". This would continue to
be true if S didn't override equals().

The point was to satisfy this part of the Liskov principle: "for all
programs P _defined in terms of T_" (emphasis mine). Perhaps I have not
achieved that satisfactorily. Also, however, I argue that asking about
the objects' classes is cheating in some sense -- if you permit that,
then Liskov forbids subclassing altogether. (Yes, I use getClass() in a
typical way in S.equals(); the demonstration does not depend on this
being the case, but it is simplified by it. Any equals() implementation
that renders an S equal to any object distinct from itself would do.)
I'm tempted to go on to discuss the invariant:

a != b -> !a.equals(b)

This is true for objects of type Object(), but overriding equals() can
violate it. Since I'm not sure if I'm missing your point entirely, I'll not
do that now.

Well, now, go ahead and provide a simpler example, why don't you? ;-)
Yes, that relationship can be used to construct a similar demonstration.
Consider, then, that similar problems do not necessarily occur in
other method overriding scenarios; according to Liskov, such problems
*never* occur in a well-formed class hierarchy. *That* is the point.


John Bollinger
(e-mail address removed)
 
M

Mike Schilling

John C. Bollinger said:
Well, now, go ahead and provide a simpler example, why don't you? ;-)

The simplest one I can think of is a StringComparator class, say, one to
sort strings lexicographically. It has no state, thus all instances are
identical [1], so logically it includes the definitions:

public int hashCode() { return 1; }
public boolean equals(Object o) { return o != null && o.getClass() ==
this.getClass(); }

The program:

Object o1 = new Object();
Object o2 = new Object();
return o1.equals(o2);

clearly always returns false for Objects, always true for StringComparators

1. It's true that this would probably use the singleton pattern with a
non-public constructor. That would make overriding equals()
and hashCode() unnecessary.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top