Java Forum / General / January 2008
Type conversion for reference types?
Peter Duniho - 09 Jan 2008 06:07 GMT 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
Stefan Ram - 09 Jan 2008 06:25 GMT >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.
Stefan Ram - 09 Jan 2008 06:53 GMT >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«.
Knute Johnson - 09 Jan 2008 06:31 GMT > 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 [quoted text clipped - 40 lines] > 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.
 Signature Knute Johnson email s/nospam/knute/
Lew - 09 Jan 2008 06:32 GMT > 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.
 Signature Lew
Peter Duniho - 09 Jan 2008 08:57 GMT 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
RedGrittyBrick - 09 Jan 2008 11:33 GMT > 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.
Roedy Green - 10 Jan 2008 07:53 GMT On Wed, 09 Jan 2008 11:33:23 +0000, RedGrittyBrick <RedGrittyBrick@SpamWeary.foo> wrote, quoted or indirectly quoted someone who said :
>> 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. See http://mindprod.com/jgloss/swing.html#AWTEQUIVALENTS for a table of AWT and Swing equivalents.
and a list of differences between them.
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Arne Vajhøj - 19 Jan 2008 05:05 GMT > 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 [quoted text clipped - 8 lines] > 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); } }
Roedy Green - 09 Jan 2008 10:16 GMT On Tue, 08 Jan 2008 22:07:33 -0800, "Peter Duniho" <NpOeStPeAdM@nnowslpianmk.com> wrote, quoted or indirectly quoted someone who 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. My essay might help explain this:
http://mindprod.com/jgloss/cast.html
 Signature Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Hal Rosser - 09 Jan 2008 20:03 GMT > 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 [quoted text clipped - 22 lines] > * 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
Peter Duniho - 09 Jan 2008 20:17 GMT > 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
Patricia Shanahan - 09 Jan 2008 21:15 GMT >> 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 [quoted text clipped - 8 lines] > > 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
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 ...
|
|
|