upcast: I have lost my example

S

Stefan Ram

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

Thomas Hawtin

Stefan said:
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.

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

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
 
S

Stefan Ram

Thomas Hawtin said:
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.
 
C

Chris Uppal

Stefan said:
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
 
S

Stefan Ram

Chris Uppal said:
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).
 
C

Chris Uppal

I said:
String s = //...
((Object)s).toString();

Sorry, please ignore that. The example is completely invalid (was thinking in
a different language)

-- chris
 
S

Stefan Ram

Chris Uppal said:
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(); }}
 
C

Chris Uppal

Stefan said:
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
 
R

Roedy Green

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

Thomas Hawtin

Stefan said:
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.

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
 
T

Thomas Hawtin

Stefan said:
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
 
S

Stefan Ram

Thomas Hawtin said:
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!
 
C

Chris Uppal

Stefan said:
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
 
S

Stefan Ram

Chris Uppal said:
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.
 
C

Chris Uppal

Stefan said:
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.

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
 
S

Stefan Ram

Chris Uppal said:
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.
 
C

Chris Uppal

Stefan said:
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
 

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,997
Messages
2,570,241
Members
46,832
Latest member
UtaHetrick

Latest Threads

Top