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

Discussion in 'Java' started by Tobias K., Nov 19, 2004.

  1. Tobias K.

    Tobias K. Guest

    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
     
    Tobias K., Nov 19, 2004
    #1
    1. Advertising

  2. Tobias K.

    Hal Rosser Guest

    "Tobias K." <> wrote in message
    news:419d3d78$...
    > 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.


    ---
    Outgoing mail is certified Virus Free.
    Checked by AVG anti-virus system (http://www.grisoft.com).
    Version: 6.0.788 / Virus Database: 533 - Release Date: 11/1/2004
     
    Hal Rosser, Nov 19, 2004
    #2
    1. Advertising

  3. "Tobias K." <> wrote in message
    news:419d3d78$...
    > 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 http://www.iviz.com/
     
    Matt Humphrey, Nov 19, 2004
    #3
  4. Tobias K.

    Tobias K. Guest

    Matt Humphrey wrote:
    > 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
     
    Tobias K., Nov 19, 2004
    #4
  5. Tobias K.

    Chris Smith Guest

    Tobias K. <> wrote:
    > 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
     
    Chris Smith, Nov 19, 2004
    #5
  6. 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.



    Chris Smith wrote:
    > Tobias K. <> wrote:
    >
    >>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.
    >
     
    Andrea Desole, Nov 19, 2004
    #6
  7. Tobias K.

    Tony Morris Guest

    Tony Morris, Nov 19, 2004
    #7
  8. Tobias K.

    Tony Morris Guest

    > 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.

    --
    Tony Morris
    http://xdweb.net/~dibblego/
     
    Tony Morris, Nov 19, 2004
    #8
  9. Tobias K.

    Chris Smith Guest

    Please don't top-post.

    Andrea Desole <_SPAM_PLEASE.nl> wrote:
    > 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
     
    Chris Smith, Nov 19, 2004
    #9
  10. Tony Morris wrote:

    >>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.


    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
     
    John C. Bollinger, Nov 19, 2004
    #10

  11. >
    >>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?


    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.
     
    Andrea Desole, Nov 19, 2004
    #11
  12. Tobias K.

    Chris Smith Guest

    Andrea Desole <_SPAM_PLEASE.nl> wrote:
    > > 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.


    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
     
    Chris Smith, Nov 19, 2004
    #12
  13. Tobias K.

    chairam Guest

    >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
     
    chairam, Nov 20, 2004
    #13
  14. "John C. Bollinger" <> wrote in message
    news:cnl2qh$8a0$...
    > (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.
     
    Mike Schilling, Nov 21, 2004
    #14

  15. > 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
     
    Andrea Desole, Nov 22, 2004
    #15
  16. Mike Schilling wrote:

    > "John C. Bollinger" <> wrote in message
    > news:cnl2qh$8a0$...
    >
    >>(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
     
    John C. Bollinger, Nov 22, 2004
    #16
  17. "John C. Bollinger" <> wrote in message
    news:cnss70$un2$...
    >
    >> 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? ;-)


    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.
     
    Mike Schilling, Nov 22, 2004
    #17
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Matt
    Replies:
    3
    Views:
    15,589
    Tony Morris
    Apr 26, 2004
  2. jstorta
    Replies:
    3
    Views:
    458
    jstorta
    Feb 20, 2006
  3. Evan Klitzke
    Replies:
    0
    Views:
    388
    Evan Klitzke
    Aug 2, 2007
  4. bart van deenen
    Replies:
    6
    Views:
    805
    bart van deenen
    Mar 3, 2009
  5. Peter
    Replies:
    6
    Views:
    270
    Peter
    Jan 5, 2012
Loading...

Share This Page