Java Forum / General / December 2005
upcast: I have lost my example
Stefan Ram - 28 Nov 2005 23:40 GMT When someone asked me "When is an explicit upcast needed?", I responded with the expression
Math.random() > 0.5 ?( Object )System.in : System.out
Bue in Java 1.5 this upcast is not required anymore. So, the expression
Math.random() > 0.5 ? System.in : System.out
will compile under 1.5 and 1.6, but not under 1.3 and 1.4.
So what would be an example of a situation where an explicit upcast of an expression with a reference type is required under Java 1.5 and 1.6?
Preferably an example that uses standard methods and identifiers as above and does not require the declaration of additional identifiers [methods].
I can think of:
interface A {} interface B {} class C implements A,B {}
public class Main { static void f( A a ){} static void f( B b ){} public static void main( final java.lang.String[] args ) { f(( A )new C() ); }}
Here the upcast "( A )" is required.
However, this example needs to declare methods and a situation with two interfaces (multiple inheritance), I would prefer an example with a single class as the super type (single inheritance) and without the need to declare additional classes or methods.
Thomas Hawtin - 29 Nov 2005 00:13 GMT > When someone asked me "When is an explicit upcast needed?", > I responded with the expression [quoted text clipped - 7 lines] > > will compile under 1.5 and 1.6, but not under 1.3 and 1.4. Good old^Wnew intersection types.
> So what would be an example of a situation where an explicit > upcast of an expression with a reference type is required [quoted text clipped - 21 lines] > super type (single inheritance) and without the need > to declare additional classes or methods. Casting from Properties to Map<String,String>, or any other "impossible" cast (given a non-buggy compiler).
Actually, you don't actually need the explicit cast. You could write:
A c = new C(); f(c);
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Stefan Ram - 29 Nov 2005 10:33 GMT >Actually, you don't actually need the explicit cast. You could >write: >A c = new C(); >f(c); For this example, I would like to use a subset of Java that excludes declarations.
I would like to answer the question "When is an upcast needed?" and preferably do not want to assume that declarations are already known to the person asking the question.
>Casting from Properties to Map<String,String>, or any other >"impossible" cast (given a non-buggy compiler). I am not able to derive an actual expression that will fail if the upcast is removed from this description.
java.util.Properties implements Map<Object,Object>, but not Map<String,String>, so I wonder if a cast to "Map<String,String>" is an "upcast" at all in the sense required here, i.e., a cast to a base class.
I am looking for an expression that needs an explicit cast of a subexpression to a base class of that subexpression. An example was
Math.random() > 0.5?( java.lang.Object )System.in : System.out
in Java 1.4. Here "System.in" is cast to a base class of java.io.InputStream, and the example does not require declarations in addition to the J2SE-API. Now I am looking for such an example in Java 1.5.
Thomas Hawtin - 29 Nov 2005 18:19 GMT > For this example, I would like to use a subset of Java that > excludes declarations. [quoted text clipped - 3 lines] > declarations are already known to the person asking the > question. You don't think that's putting the cart before the horse? From 1.5 casts of reference types should be a relatively obscure feature. Perhaps mostly seen in implementations of equals methods.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Stefan Ram - 29 Nov 2005 22:44 GMT >You don't think that's putting the cart before the horse? From >1.5 casts of reference types should be a relatively obscure >feature. Perhaps mostly seen in implementations of equals >methods. My main reason to want to explain casts in my classes is to prepare the explanation of implicit type conversions and to introduce the concept of the modification of the type of an expression. So first, I would like to explain the concept of a type modification by:
( Object )"example"
Then I will show how to name a reference
final Object reference =( Object )"example";
and eventually I will mention that here the upcast "( Object )" is not required, because it is implied:
final Object reference = "example";
But I believe having first seen the explicit upcast helps to visualize what is happening implicitly in the last case.
Now, of course, people might ask "What does one need the upcast operator for?" - I then would like to show an example at a point in the tutorial where many concepts have not yet been introduced, not even arrays. (So I can not use the examples with arrays, which some authors have kindly suggested to me in this thread.)
So I should best try to find an overloaded standard method with different behavior for an Object and a String argument.
Thanks for all replys!
Chris Uppal - 30 Nov 2005 09:04 GMT > So I should best try to find an overloaded standard method > with different behavior for an Object and a String argument. Why not the very-simple-indeed:
String hi = "hello"; String s1 = new String(hi); // works String s2 = new String((Object) hi); // oops!
Personally, I suspect that might be confusing to many -- but then that (I also suspect) hints that this concept is not suitable for early introduction.
-- chris
Stefan Ram - 30 Nov 2005 10:26 GMT >Why not the very-simple-indeed: >String hi = "hello"; >String s1 = new String(hi); // works >String s2 = new String((Object) hi); // oops! This example can be used indeed to show that the cast does have an effect at all. However, when someone asks "what is it good for?" an example that would fail /without an upcast/ would be even better.
>Personally, I suspect that might be confusing to many -- but >then that (I also suspect) hints that this concept is not >suitable for early introduction. The sequence of topics in my tutorial was developed by me to be quite strictly sorted according to the dependency, so that every concept is introduced before it is required for another concept.
Because I believe, that beginners should like to program something graphical, I try to teach things so that Swing can be introduced as soon as possible. So before the Swing chapter I teach in this sequence (the audience are people not expected to have ever programmed before):
- primitive values, primitive types and literals - static method invocations and the message syntax "receiver.selector( arguments )" where "receiver" is a class - operators ("+", "/", ...) - expression statement, compound-statement, ?:-operator - writing static methods, classes with static methods, constants and static fields/variables - using objects and understanding reference types of expressions and objects - understanding the concepts of subtypes and interfaces - writing classes with non-static methods and fields, writing interfaces
And then I can begin to teach how to build basic Swing-GUIs.
A remarkable thing, for example, is that at this point I have not yet mentioned things like the if-, switch-statement or any loop-statements. Because of the limited time some things do not fit in the course and so I dropped those.
Chris Uppal - 30 Nov 2005 17:24 GMT > > String s1 = new String(hi); // works > > String s2 = new String((Object) hi); // oops! [quoted text clipped - 3 lines] > it good for?" an example that would fail /without an upcast/ > would be even better. Wouldn't the answer "not much" be better still ? After all, you are not aiming to introduce your students to all of the Java language, so I don't understand why you want to put emphasis on this particular (rather arcane) feature. Why mention upcasts at all ?
> [...] > A remarkable thing, for example, is that at this point I have > not yet mentioned things like the if-, switch-statement or any > loop-statements. That's impressive. (Quite seriously. I have a sneaking suspicion that those features are overused by nearly all programmers -- including myself).
-- chris
Stefan Ram - 30 Nov 2005 22:57 GMT >Why mention upcasts at all ? I was hoping that discussing explicit upcasts will help to understand implied type conversions done by Java, when an expression of a subtype is accepted at places where a value of a supertype is expected.
Chris Uppal - 01 Dec 2005 14:18 GMT > I was hoping that discussing explicit upcasts will help to > understand implied type conversions done by Java, when an > expression of a subtype is accepted at places where a value > of a supertype is expected. Hmm. Well, you are a teacher, and I am not.
I would have tried to explain that it's OK to assign a reference to a Float to a variable of type Number. (though, probably not in those words) and that the Float itself is unchanged, but the compiler "forgets" that it knew the object was a Float, and left it at that. To my mind that "it just works how you expect, you don't have to think about why" is pretty much the /central/ message of declaratively-typed OO programming.
S) Can I assign a Float to a Number ?
T) <surprised>Yes, of course. Why not ?
S) <still puzzled> But I can't assign to a String to a Number, so how come it works with Floats ?
T) Because everything you can do with a Number you can also do with a Float, so nobody cares if you use a Float instead of a Number.
(if you see what I mean)
-- chris
Thomas Hawtin - 29 Nov 2005 19:25 GMT >>Casting from Properties to Map<String,String>, or any other >>"impossible" cast (given a non-buggy compiler). > > > I am not able to derive an actual expression that will > fail if the upcast is removed from this description. The statement:
Map<String,String> map = (Map<String,String>)new Properties();
should fail to compile, producing a message along the lines of:
MapProp.java:4: inconvertible types found : java.util.Properties required: java.util.Map<java.lang.String,java.lang.String> Map<String,String> map = (Map<String,String>)new Properties(); ^ 1 error
However, Sun's 1.5 compiler appears to be buggy in this respect. Discussed in another thread yesterday.
> java.util.Properties implements Map<Object,Object>, but not > Map<String,String>, so I wonder if a cast to > "Map<String,String>" is an "upcast" at all in the sense > required here, i.e., a cast to a base class. A cast to either Map or Object should be required (given a correct compiler).
> I am looking for an expression that needs an explicit cast > of a subexpression to a base class of that subexpression. > An example was > > Math.random() > 0.5?( java.lang.Object )System.in : System.out The expression
new StringBuffer().append((Object)new char[] { '?' })
should give a different result without the cast, but still compile.
I tried to create something really obscure using TreeMap.Entry, but failed.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Chris Uppal - 29 Nov 2005 11:22 GMT > So what would be an example of a situation where an explicit > upcast of an expression with a reference type is required > under Java 1.5 and 1.6? Two examples:
1) You require a readable (by a programmer) representation of Strings which allows you to distinguish between equal but not identical instances.
String s = //... ((Object)s).toString();
2) You wish to pass an Object[] array to a varadic method as a single argument, rather than having it interpreted as a pre-packed valist of Objects:
Method method = // reflective access to // java.util.Arrays.hashCode(Object[]) Object[] data = //... the data Object hash = method.invoke(null, (Object)data);
(Idea suggested by John Bollinger)
-- chris
Chris Uppal - 29 Nov 2005 11:53 GMT I wrote:
> String s = //... > ((Object)s).toString(); Sorry, please ignore that. The example is completely invalid (was thinking in a different language)
-- chris
Stefan Ram - 29 Nov 2005 11:56 GMT >Sorry, please ignore that. OK, then please also ignore my other reply to that.
Stefan Ram - 29 Nov 2005 13:45 GMT >>String s = //... >>(Object)s).toString(); >Sorry, please ignore that. The example is completely invalid >(was thinking in a different language) One does not have to change the language, however, to see that effect, one only needs to use "static":
class A { public static void f() { java.lang.System.out.println( "A" ); }}
class B extends A { public static void f() { java.lang.System.out.println( "B" ); }}
public class Main { public static void main( final java.lang.String[] args ) { new B().f(); (( A )new B() ).f(); }}
Stefan Ram - 29 Nov 2005 11:54 GMT >1) You require a readable (by a programmer) representation of Strings which >allows you to distinguish between equal but not identical instances. >String s = //... >((Object)s).toString(); I used to believe that such a cast will only make the compiler check that the signature exists in the base class, but will not alter the run time behavior.
public class Main { public static void main( final java.lang.String[] args ) { java.lang.System.out.println( "a".toString() ); java.lang.System.out.println((( java.lang.Object )"a" ).toString() ); }}
Prints "a" twice, and the two expression statements seem to be compiled to the same byte code. To get the behavior you might have intended, I might use something like:
public java.lang.String myToString( final java.lang.Object object ) { return object.getClass().getName() + "@" + Integer.toHexString( System.identityHashCode( object )); }
>Object hash = method.invoke(null, (Object)data); That might answer my question -- still I would like to know if someone knows any example even simpler (not using reflection).
Chris Uppal - 29 Nov 2005 15:46 GMT > > Object hash = method.invoke(null, (Object)data); > > That might answer my question -- still I would like to know if > someone knows any example even simpler (not using reflection). Well that (as an example) doesn't actually /use/ reflection, it's just that java.reflect.Method.invoke() is the first example that came to mind of a varadic Object* method that uses all its arguments. You can do exactly the same sort of thing with printf().
Object[] data = new Object[0]; System.out.printf("%s%n", data); System.out.printf("%s%n", (Object)data);
The first form fails at runtime (not enough arguments), the second runs fine. Hence the behaviour is different when the upcast is included.
Another class of examples is when the upcast influences method resolution. Given:
void aMethod(Object o) { ... }
void aMethod(String s) { ... }
a call to:
aMethod((Object)"hello")
will be resolved differently from a call to:
aMethod("hello").
I'll leave you to find a pre-existing example of such a pair of method definitions in the standard library, though.
Come to think of it, completely different class of examples:
void aMethod() { throw new Error(); }
compiles fine, but:
void aMethod() { throw (Throwable)(new Error()); }
is rejected.
-- chris
Roedy Green - 29 Nov 2005 18:01 GMT >>((Object)s).toString(); > > I used to believe that such a cast will only make the compiler > check that the signature exists in the base class, but will > not alter the run time behavior. It executes the Object.toString virtual method which is overridden by String, which is defined as "this object (which is already a string!) is itself returned."
In other words the dance does nothing but get you back to s in a round-about way.
 Signature Canadian Mind Products, Roedy Green. http://mindprod.com Java custom programming, consulting and coaching.
Free MagazinesGet 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 ...
|
|
|