Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / General / July 2007

Tip: Looking for answers? Try searching our database.

If the object is an instance of a class, why was it cast?

Thread view: 
metaperl - 06 Jul 2007 04:51 GMT
re: http://java.sun.com/docs/books/tutorial/java/IandI/objectclass.html

// why cast obj if it is an instance of Book?
// if it is an instance of Book, then it must have the method
available to it,
// either directly in its class or via dispatch to its parent classes

public boolean equals(Object obj) {
       if (obj instanceof Book)
           return ISBN.equals((Book)obj.getISBN());
       else
           return false;
   }
Twisted - 06 Jul 2007 05:55 GMT
> re:http://java.sun.com/docs/books/tutorial/java/IandI/objectclass.html
>
[quoted text clipped - 9 lines]
>             return false;
>     }

Because javac is stone dumb. It would really be nice if it obviated
the need for such casts inside if branches based on instanceof and
wherever else the object's run-time type is easily shown by static
analysis to be more specific than the reference's type. Unfortunately,
the compiler has roughly the IQ of a turnip, so it still thinks obj
might be a String or who-knows-what when compiling that line.

Actually, it would be nice if there was a way to avoid common
boilerplate code in equals methods. Say specifying an equals method in
a class without "abstract" or a body made it generate an equals
equivalent to

public boolean equals (Object obj) {
   if (obj == this) return true;
   if (obj == null) return false;
   if (!obj instanceof WhateverClass) return false;
   WhateverClass wc = (WhateverClass)obj;
   if (wc.fieldOne != fieldOne && (fieldOne == null || wc.fieldOne ==
null || !fieldOne.equals(wc.fieldOne))) return false;
   if (wc.fieldTwo != fieldTwo && (fieldTwo == null || wc.fieldTwo ==
null || !fieldTwo.equals(wc.fieldTwo))) return false;
   ...
   return true;
}

Of course, it might reduce some of those to if (wc.fieldThree !=
fieldThree) for a field type that's final or private and doesn't
override Object's equals(). It would ignore transient fields and use
all the others. It might do this only if the equals() being overridden
is Object's, and otherwise use

public boolean equals (Object obj) {
   if (obj == this) return true;
   if (obj == null) return false;
   if (!super.equals(obj)) return false;
   if (!obj instanceof WhateverClass) return false;
   WhateverClass wc = (WhateverClass)obj;
   if (wc.fieldOne != fieldOne && (fieldOne == null || wc.fieldOne ==
null || !fieldOne.equals(wc.fieldOne))) return false;
   if (wc.fieldTwo != fieldTwo && (fieldTwo == null || wc.fieldTwo ==
null || !fieldTwo.equals(wc.fieldTwo))) return false;
   ...
   return true;
}

using the superclass equals and then comparing the nontransient fields
specific to the subclass.

And of course you'd want to be able to autogenerate hashCode the same
way, getting some reasonable thing using all the nontransient fields
(and when super.hashCode() isn't Object.hashCode(), super.hashCode())
to generate the hash, multiplying each field's hashCode() by a random
value (hash of the field's name?) and summing the results.

This gives reasonable and correct equals and hashCode behavior for a
large chunk of the likely cases. The rest can be overridden in the
existing way. Another option, probably cleaner if less powerful, is
just to have one new methods in Object:

@SuppressWarnings("unchecked")
protected final boolean <T> typicalEquals (Object obj, Class<T> base)
{
   if (this == obj) return true;
   if (obj == null) return false;
   if (!base.isAssignableFrom(obj.getClass())) return false;
   return ((EqualityComparable<? super T>)this).equalTo((T)obj);
}

Add this interface:

public interface EqualityComparable <T> {
   boolean equalTo (T obj);
}

A subclass Foo that overrides Object's equals can then implement
EqualityComparable<Foo> and write:

public Object equals (Object obj) {
   return typicalEquals(obj, Foo.class);
}

public boolean equalTo (Foo obj) {
 if (!obj.getClass().equals(Foo.class)) return obj.equalTo(this);
 if (obj.fieldOne != fieldOne && (fieldOne == null || obj.fieldOne ==
null || fieldOne.equals(obj.fieldOne))) return false;
 if (obj.intField != intField) return false;
 if (!obj.neverNullField.equals(neverNullField) return false;
 return true;
}

with the class-specific guts of the equals test in equalTo.

Then its own subclasses can just override equalTo, and possibly use
"if (!super.equalTo(obj)) return false;" when appropriate.

The effect of the above is that Foo is unequal to null or to any non-
Foo object. It is equal to any Foo it is identical to. It is also
equal to any Foo for which equalTo(Foo) returns true, but to no other
Foos.

The line
 if (!obj.getClass().equals(Foo.class)) return obj.equalTo(this);

means that if a vanilla Foo (or one that doesn't override Foo's
equalTo) is compared to a subclass instance the subclass equalTo is
used instead. Of course this is an infinite recursion if two Foo
subclasses both fail to override equalTo. This might not be a good
idea depending -- you might want real double dispatch, or for Foo to
know how to compare two Foos regardless of subclass, even subclasses
not known of by the writer of Foo. Whether this is feasible depends on
what Foo really is, and these same issues arise with writing equals()
methods already.

And obviously you want Foo's hashCode() to be consistent with the
equalTo() method here...

ClassCastException can be thrown for three reasons here. One, if it's
thrown in equalTo for any reason. Two, if you call typicalEquals
without implementing EqualityComparable. And three, if you call
typicalEquals(obj, SomeClass.class) in a class that doesn't implement
EqualityComparable<AnotherClass> where AnotherClass is, or is a
supertype of, SomeClass. (The object obj is silently cast to
AnotherClass when equalTo(AnotherClass) is called with it as argument,
due to the unchecked cast of obj to T. Since obj is only sure to be of
SomeClass, though this is assured by getting past the if(! ...
assignableFrom ...) return false line, this means SomeClass has to be
assignable to AnotherClass. If that isn't the case, essentially the
error is that "this" is not in fact assignable to EqualityComparable<?
extends T> when cast to same inside typicalEquals.)

NullPointerException should only be thrown if your equalTo method
throws it.

Disclaimer: the above hasn't really been tested in any way and might
require slight adjustment, but the basic premise looks sound.
bencoe@gmail.com - 06 Jul 2007 06:30 GMT
>It would really be nice if it obviated
>the need for such casts inside if branches based on instanceof and
>wherever else the object's run-time type is easily shown by static
>analysis to be more specific than the reference's type.

I think that this would make things quite a bit more confusing, if I
had to modify my coding style to take into account the fact that I'm
in, for instance, an "instanceof" block. I'd much rather go to the
trouble of habitually casting things -- this really isn't very much
trouble.

In answer to the original question: you have to cast it because, like
Twisted says, the runtime environment is currently only aware that it
has been passed something of the superclass Object -- giving it access
only to the following methods until you cast,

http://java.sun.com/j2se/1.3/docs/api/java/lang/Object.html

------
Ben
http://www.plink-search.com
Lew - 07 Jul 2007 16:04 GMT
>> It would really be nice if it obviated
>> the need for such casts inside if branches based on instanceof and
[quoted text clipped - 13 lines]
>
> http://java.sun.com/j2se/1.3/docs/api/java/lang/Object.html

This is a slight misstatement of the situation.  Java distinguishes between
the variable, whose compile-time type only is known, and the referenced
object, whose full run-time type is known.  This has nothing to do with "javac
... having the IQ of a turnip" but is a deliberate Java feature to support
disciplined coding and the idioms of polymorphism.

The variable of "Object" type can only refer to methods known to the "Object"
type because that is the type of the variable.  Turnip-brained or otherwise,
there is no way at compile time to determine that obj should have access to
methods of other types.  By declaring the variable "Object", the programmer is
deliberately telling the compiler only to make Object attributes available to
it.  To infer otherwise is for the compiler to predict all runtime scenarios -
not feasible.

Casting the expression to a subtype informs the compiler that, if the cast is
permitted, the expression has access to the attributes and methods of that
subtype.

One should not denigrate this valuable and useful aspect of the Java language.

Signature

Lew

Twisted - 07 Jul 2007 22:04 GMT
> One should not denigrate this valuable and useful aspect of the Java language.

I never did. I think you misunderstood my earlier post, or lost track
of the context, which was inside a branch condition using
"instanceof".

It wouldn't be too big a problem in static analysis for the compiler
to consider foo to be a Book inside

if (foo instanceof Book) {
 ...
}

:)

Of course, most usually you want polymorphism rather than instanceof
conditionals, but sometimes (such as trying to make something act
polymorphic also in an argument type, like math operations) you may
need to.
Manivannan Palanichamy - 06 Jul 2007 12:01 GMT
> re:http://java.sun.com/docs/books/tutorial/java/IandI/objectclass.html
>
[quoted text clipped - 9 lines]
>             return false;
>     }

(Book)obj.getISBN() -- this expression can be rewritten like,
Book tmp = (Book)obj;
tmp.getISBN();   the compiler is very sure that, tmp is an instance of
Book and it has the method getISBN(), so the compilation succeeds.

But, if you dont cast it, and just make the statement like,
obj.getISBN()  -- the compiler does not find the getISBN() method in
Object class, and simply blasts. Its doing it's job very well
actually.

Thing is, the poor compiler cannot predict, which object, the
reference can point to, at run time. Thats why you need a cast. But,
this hurdle is removed in Java 1.5. Yes, read about Generics of Java
1.5.

--
Manivannan.Palanichamy (@) Oracle.com
http://mani.gw.googlepages.com/index.html
Hendrik Maryns - 11 Jul 2007 11:21 GMT
Manivannan Palanichamy schreef:
>> re:http://java.sun.com/docs/books/tutorial/java/IandI/objectclass.html
>>
[quoted text clipped - 13 lines]
> Book tmp = (Book)obj;
> tmp.getISBN();

No, it cannot!  Actually, I am pretty sure it will give a
ClassCastException at runtime, unless the ISBN is also an instance of
Book, which seems unlikely.  It will probably not even compile with a
symbol not found error, since obj will probably not have the method
getISBN() (I assume it is an Object).

What you are describing here is equivalent to

((Book)obj).getISBN();

Note how the obj is cast to book, and only then the getISBN() is called
/on the cast object/.

I suppose the OP made an error when copy/pasting(??) the example code.

> --
> Manivannan.Palanichamy (@) Oracle.com

Your signature is broke: there has to be a space after the two dashes!

Cheers, H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
http://aouw.org
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
Roedy Green - 06 Jul 2007 13:23 GMT
>// why cast obj if it is an instance of Book?
>// if it is an instance of Book, then it must have the method
[quoted text clipped - 7 lines]
>            return false;
>    }

see http://mindprod.com/jgloss/cast.html
--
Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com
metaperl - 06 Jul 2007 14:07 GMT
On Jul 6, 8:23 am, Roedy Green <see_webs...@mindprod.com.invalid>
wrote:

> >// why cast obj if it is an instance of Book?
  }

> see http://mindprod.com/jgloss/cast.html

ah, a Mollifying cast! thank you!


Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.