Type conversion for reference types?

P

Peter Duniho

My apologies if this is a silly question in the context of Java. I'm very
new to the language, though I've got lots of experience in others
(including C#, which has borrowed a lot from Java) and programming
generally.

I am wondering what facility for type conversion of reference types, if
any, exists in Java. And more specifically, if none exists, how do I
reliably deal with situations where I want a specific sub-class but am
only given some base class. I mean, is there anything unique to Java that
provides for this, or is it really just as simple as "casting only works
if the actual instance type is compatible with the destination type" (for
example, in C# you can write explicit type conversions that allow
arbitrary code to execute to convert from one type to another).

The two particular examples are Graphics and Graphics2D, and Rectangle2D
and Rectangle2D.Double.

With respect to Graphics/Graphics2D, I've seen lots of examples of AWT
code where in a paint() method, the Graphics instance passed in is cast to
Graphics2D. I've done this myself in my own code and it appears to work
fine. But:

* How can I be guaranteed that the cast will succeed?
* Is there a type conversion that will ensure it always will?
* Or will the code fail if it's run in an environment where a plain
Graphics instance is passed?

The second example comes up in the context of using an Area object. I
want to be able to get the bounding rectangle from the Area object and use
the exact location and size of the rectangle to do some geometric
contraining code. But Area.getBounds() returns a Rectangle2D, which
doesn't expose the fields that provide that information (they're in the
sub-classes).

The Rectangle class (base class for Rectangle2D) does have "get" methods
for these values (getX(), getY(), getWidth(), getHeight()) and I can use
those. But for grins I tried casting the Rectangle2D to a
Rectangle2D.Double and it worked fine. Again, I'm curious of pretty much
the same questions I have for the Graphics class. Is this something I can
either count on, or force to always happen? Or did I just luck out?

Thanks,
Pete
 
S

Stefan Ram

Peter Duniho said:
I am wondering what facility for type conversion of reference
types, if any, exists in Java.

The type of an expression can be set by a cast.
And more specifically, if none exists, how do I reliably deal
with situations where I want a specific sub-class but am only
given some base class.

This is done by a downcast and treated in most textbooks.

For example, »Casting Objects« in

http://java.sun.com/docs/books/tutorial/java/IandI/subclasses.html
provides for this, or is it really just as simple as "casting only works
if the actual instance type is compatible with the destination type"

In the expression »(type)reference«, where »type« is a type
name and »reference« is a reference expression, the reference
must evaluate to null or the type of the object refered to
must be a subtype of the type »type«. (1)
* How can I be guaranteed that the cast will succeed?

An upcast always succeeds, once the program was compiled.
The corresponding type conversion can also be done
implicitly, without a cast operator.

For a downcast, you need to make sure that (1) holds.
This is being tested at runtime and will throw
an exception otherwise.
 
K

Knute Johnson

Peter said:
My apologies if this is a silly question in the context of Java. I'm
very new to the language, though I've got lots of experience in others
(including C#, which has borrowed a lot from Java) and programming
generally.

I am wondering what facility for type conversion of reference types, if
any, exists in Java. And more specifically, if none exists, how do I
reliably deal with situations where I want a specific sub-class but am
only given some base class. I mean, is there anything unique to Java
that provides for this, or is it really just as simple as "casting only
works if the actual instance type is compatible with the destination
type" (for example, in C# you can write explicit type conversions that
allow arbitrary code to execute to convert from one type to another).

The two particular examples are Graphics and Graphics2D, and Rectangle2D
and Rectangle2D.Double.

With respect to Graphics/Graphics2D, I've seen lots of examples of AWT
code where in a paint() method, the Graphics instance passed in is cast
to Graphics2D. I've done this myself in my own code and it appears to
work fine. But:

* How can I be guaranteed that the cast will succeed?
* Is there a type conversion that will ensure it always will?
* Or will the code fail if it's run in an environment where a plain
Graphics instance is passed?

The second example comes up in the context of using an Area object. I
want to be able to get the bounding rectangle from the Area object and
use the exact location and size of the rectangle to do some geometric
contraining code. But Area.getBounds() returns a Rectangle2D, which
doesn't expose the fields that provide that information (they're in the
sub-classes).

The Rectangle class (base class for Rectangle2D) does have "get" methods
for these values (getX(), getY(), getWidth(), getHeight()) and I can use
those. But for grins I tried casting the Rectangle2D to a
Rectangle2D.Double and it worked fine. Again, I'm curious of pretty
much the same questions I have for the Graphics class. Is this
something I can either count on, or force to always happen? Or did I
just luck out?

Thanks,
Pete

If it isn't it won't is the rule. And there are a lot hidden in there
like the Graphics and Graphics2D.
 
L

Lew

Peter said:
I am wondering what facility for type conversion of reference types, if
any, exists in Java. And more specifically, if none exists, how do I
reliably deal with situations where I want a specific sub-class but am
only given some base class. I mean, is there anything unique to Java
that provides for this, or is it really just as simple as "casting only
works if the actual instance type is compatible with the destination type"

Downcasts are legal, upcasts are unnecessary.

You can always cast from a base type, e.g., Number, down to the actual type of
the object at run-time, or anything in between:

Number someNumber = getItSomehow();
Long lval = (Long) someNumber;

Of course, if someNumber is actually a Double, you'll raise a ClassCastException.

Upcasting is a "widening conversion" and doesn't require explicit syntax:

Number num = Long.valueOf( 1L );
(for example, in C# you can write explicit type conversions that
allow arbitrary code to execute to convert from one type to another).

Not so, Java. You can in some situations write a constructor or factory
method that generates an equivalent instance in a different class:

Number num = Long.valueOf( "1" );

You need to call this method everywhere it matters.

String momsAge = request.getParameter( "momsAge" );
Person mom = new Mom();
mom.setAge( Long.valueOf( momsAge ));

You never know. Cryogenics, advances in stem cells, genetics, they'll cure
all viral diseases - better to give enough range.
With respect to Graphics/Graphics2D, I've seen lots of examples of AWT
code where in a paint() method, the Graphics instance passed in is cast
to Graphics2D. I've done this myself in my own code and it appears to
work fine. But:

* How can I be guaranteed that the cast will succeed?

Someone up in Valhalla said, "Trust us!"

Don't worry, if they ever change it then your program will blow up with a
ClassCastException, so you'll know.
* Is there a type conversion that will ensure it always will?

It's called, "The actual object always (coincidentally) happens to be a
Graphics2D." In other words, no. But you're talking about the Swing API,
which happens to use only Graphics2D objects, so you're good.

Here's one for people who complain about Java's type safety. You are asking
for more type safety - a more restrictive type in the method signature. As
well you should - it is very much a hack that type safety is circumvented
here. I believe it has to do with needing to override AWT methods, which do
not guarantee to have Graphics2D arguments, and thus not being able to change
the signature. So the Swing hack is not to promise you anything, but give you
(nudge, nudge, wink, wink) a Graphics2D on the sly.
* Or will the code fail if it's run in an environment where a plain
Graphics instance is passed?

These are Swing methods. Swing methods will do what they're documented to do.
Cast away with impunity.

You could, I suppose, write a custom component that somehow subverts the
promise. Don't do that.

I leave the other questions to those more expert than I.
 
S

Stefan Ram

This is done by a downcast and treated in most textbooks.

I would like to elaborate:

Types of expressions are known at compile time, while
types of objects are known only at run time in the general case.

Let r be a reference-valued expression (syntactically, a
»UnaryExpressionNotPlusMinus«).

Let T be a reference type (syntactically, a »ReferenceType«).

Then

(T)r

is an expression, called a »cast expression« (»CastExpression«).

If the value of »r« is null, then the value of the cast
expression is the null reference cast to T.

Otherwise, the value of the expression r refers to an object.

Let O be the type of this object. (O is a class.)
Let R be the type of r.

A type »T« here is called a »supertype« of a type »S«
iff T is equal to S or
iff »S« directly or indirectly extends or implements T.

A reference expression can only be used to evaluate
to a reference value that refers an object if the type
of the reference expression is a supertype of the
type of the object.

Therefore, we require that in »(T)r«, T is a supertype of O
(the object refered to by the value of »r«).

If T is a supertype of R, this holds. Such a cast is called
an »upcast«.

If R is a supertype of T and not equal to T, then whether T
is a supertype of O in the general case can only be known
at runtime, because the type of O is only known at run time,
except that it is already known that it must be a subtype
of R. Such a cast is called a »downcast«.
 
P

Peter Duniho

Okay, so bottom line: no implicit type conversions (that is, something
that actually modifies the data) for reference types. Only straight
casting.

Thanks for the explanation, even if it does still leave me with the
uncomfortable question of "how do I know what I'm really getting?"

And one other thing you mentioned:

[...]
* Is there a type conversion that will ensure it always will?

It's called, "The actual object always (coincidentally) happens to be a
Graphics2D." In other words, no. But you're talking about the Swing
API, which happens to use only Graphics2D objects, so you're good.

Am I talking about the Swing API?

I made a specific choice to try to avoid Swing. The program I'm writing
isn't going to use any advanced controls anyway, and I'd prefer that what
controls I do use be using native controls on each platform, to retain
native look-and-feel.

(As an aside, the three main APIs I looked at were AWT, Swing, and SWT.
SWT has so far produces far superior graphical rendering for me, but I'm
put off by the need to deliver a separate library for each platform I want
to support. With AWT and Swing, if a recent enough Java is installed, the
same Java program should run on any platform, as I understand it. From
what I've read, Swing reimplements all of the controls and always will
look the same regardless of the host OS).

Graphics and Graphics2D are both in java.awt, and I just assumed that
Graphics2D was actually part of AWT. Am I using Swing without realizing
it? I understand that my reason for avoiding Swing is based on the Swing
controls, and so as long as I'm not using those the other parts of Swing
aren't actually something I need to avoid (it's not like I'm looking to
avoid linking to some library or something like that :) ). But I'm a bit
confused about where the line between AWT and Swing is drawn, if at all.

As far as the specific example goes...

While I'm not exactly clear on why they didn't just allow the paint()
method to be overloaded, I do admit it provides a simpler API. But at the
very least, the docs should actually _say_ what is being passed to the
paint() method. (By docs, I'm talking about this:
http://java.sun.com/javase/6/docs/api/ ...that's what I've been using as
my reference).

For example, the page for Graphics2D might have mentioned it somewhere in
the lengthy discussion describing drawing in Java. Or the page for the
Component class (where paint() is declared) might mentioned that the
system will pass a Graphics2D as the parameter, at least on Java versions
recent enough to do that. Or maybe it would at least have mentioned it in
the article entitled "Painting in AWT and Swing"
(http://java.sun.com/products/jfc/tsc/articles/painting/index.html).

But I haven't found a single mention of the exact behavior of the
implementation anywhere in what I think are the official Java docs.

Anyway, thanks very much for the information. I've actually run into
other oddities that, while not preventing me from implementing the code
the way I want to, do have me scratching my head now and then. Maybe if
they keep puzzling me, I'll post those questions too, eventually. :)

Pete
 
R

RedGrittyBrick

Peter said:
Okay, so bottom line: no implicit type conversions (that is, something
that actually modifies the data) for reference types. Only straight
casting.

There is always explicit type conversion. For example List.toArray() or
Integer.toString();
From what I've read, Swing reimplements all of the
controls and always will look the same regardless of the host OS).

You can use a system Look and Feel so that the application looks
(almost) like a native application on (almost) any platform.

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

Graphics and Graphics2D are both in java.awt, and I just assumed that
Graphics2D was actually part of AWT. Am I using Swing without realizing
it?

Probably not, java.awt.Graphics2D is AWT not Swing. AIUI Swing uses a
lot of AWT but that doesn't make AWT Swing.

javax.swing.Graphics2D would be Swing if it existed. But then you'd know
you were using Swing.

I'm a bit confused about where the line between AWT and Swing is
drawn, if at all.

I bet Roedy's web site has a page on this.

Swing is built on AWT. For example look at
http://java.sun.com/j2se/1.5.0/docs/api/javax/swing/JMenuBar.html
java.lang.Object
java.awt.Component
java.awt.Container
javax.swing.JComponent
javax.swing.JMenuBar
Many Swing Objects are descended from AWT objects. AIUI the reverse
never applies.

When you write a Swing application you end up using a lot of AWT
objects. For example Swing's JMenu.setMargin() takes an AWT
java.awt.Margin parameter. AIUI the reverse never applies.

In practice, when using an IDE like Eclipse or NetBeans, you never have
to worry about this IME.

A rule of thumb is that all Swing visual components have names starting
with the letter J. So far as I know, I've never accidentally inserted an
an AWT TextField instead of a Swing JTextField into a Swing GUI.
 
H

Hal Rosser

Peter Duniho said:
My apologies if this is a silly question in the context of Java. I'm very
new to the language, though I've got lots of experience in others
(including C#, which has borrowed a lot from Java) and programming
generally.

I am wondering what facility for type conversion of reference types, if
any, exists in Java. And more specifically, if none exists, how do I
reliably deal with situations where I want a specific sub-class but am
only given some base class. I mean, is there anything unique to Java that
provides for this, or is it really just as simple as "casting only works
if the actual instance type is compatible with the destination type" (for
example, in C# you can write explicit type conversions that allow
arbitrary code to execute to convert from one type to another).

The two particular examples are Graphics and Graphics2D, and Rectangle2D
and Rectangle2D.Double.

With respect to Graphics/Graphics2D, I've seen lots of examples of AWT
code where in a paint() method, the Graphics instance passed in is cast to
Graphics2D. I've done this myself in my own code and it appears to work
fine. But:

* How can I be guaranteed that the cast will succeed?
* Is there a type conversion that will ensure it always will?
* Or will the code fail if it's run in an environment where a plain
Graphics instance is passed?

The getClass() method of the Object class and the getName() method of the
Class class along with the typeOf operator may be of some help in your
quest.
HTH
Hal
 
P

Peter Duniho

The getClass() method of the Object class and the getName() method of the
Class class along with the typeOf operator may be of some help in your
quest.

Thanks! I was more looking for a discussion of the rules governing what
types may be passed or returned in contexts where a base type is specified
but a derived type is actually provided. However, your information about
run-time type checking is very useful as well. I probably could've found
it myself in the docs, but having it handed over to me without me having
to work for it is very nice. :)

Pete
 
P

Patricia Shanahan

Peter said:
Thanks! I was more looking for a discussion of the rules governing
what types may be passed or returned in contexts where a base type is
specified but a derived type is actually provided. However, your
information about run-time type checking is very useful as well. I
probably could've found it myself in the docs, but having it handed
over to me without me having to work for it is very nice. :)

Pete

Also, there is the "instanceof" operator:

if(x instanceof Graphics2D){
Graphics2D y = (Graphics2D)x;
// Do Graphics2D things with y
}else{
// Do something else that only needs Graphics
}

Patricia
 
A

Arne Vajhøj

Peter said:
I made a specific choice to try to avoid Swing. The program I'm writing
isn't going to use any advanced controls anyway, and I'd prefer that
what controls I do use be using native controls on each platform, to
retain native look-and-feel.

(As an aside, the three main APIs I looked at were AWT, Swing, and SWT.
SWT has so far produces far superior graphical rendering for me, but I'm
put off by the need to deliver a separate library for each platform I
want to support. With AWT and Swing, if a recent enough Java is
installed, the same Java program should run on any platform, as I
understand it. From what I've read, Swing reimplements all of the
controls and always will look the same regardless of the host OS).

Have you tried playing with look and feel.

The demo program below illustrates.

Arne

=============================================

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

public class MultiLookAndFeel extends JFrame implements ActionListener {
private JButton windows = new JButton("Windows");
private JButton motif = new JButton("Motif");
private JButton metal1 = new JButton("Metal/ocean");
private JButton metal2 = new JButton("Metal/steel");
private JButton gtk = new JButton("GTK");
private JButton java = new JButton("Java");
private JButton system = new JButton("System");
public MultiLookAndFeel() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setLayout(new GridLayout(7, 1));
windows.addActionListener(this);
getContentPane().add(windows);
motif.addActionListener(this);
getContentPane().add(motif);
metal1.addActionListener(this);
getContentPane().add(metal1);
metal2.addActionListener(this);
getContentPane().add(metal2);
//gtk.addActionListener(this);
getContentPane().add(gtk);
java.addActionListener(this);
getContentPane().add(java);
system.addActionListener(this);
getContentPane().add(system);
pack();
}
public void actionPerformed(ActionEvent e) {
try {
if(e.getSource() == windows) {

UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
} else if(e.getSource() == motif) {

UIManager.setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
} else if(e.getSource() == metal1) {
javax.swing.plaf.metal.MetalLookAndFeel.setCurrentTheme(new
javax.swing.plaf.metal.OceanTheme());

UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
} else if(e.getSource() == metal2) {
javax.swing.plaf.metal.MetalLookAndFeel.setCurrentTheme(new
javax.swing.plaf.metal.DefaultMetalTheme());

UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
} else if(e.getSource() == gtk) {

UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
} else if(e.getSource() == java) {

UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
} else if(e.getSource() == system) {

UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
} catch (InstantiationException e1) {
e1.printStackTrace();
} catch (IllegalAccessException e1) {
e1.printStackTrace();
} catch (UnsupportedLookAndFeelException e1) {
e1.printStackTrace();
}
SwingUtilities.updateComponentTreeUI(this);
pack();
}
public static void main(String[] args) {
MultiLookAndFeel f = new MultiLookAndFeel();
f.setVisible(true);
}
}
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top