4

I'm trying to figure out why it is allowed to downcast a Graphics instance to a Graphics2D instance.

It's usually against the rules to downcast a reference type that isn't inheriting the target type.

In the graphic-oriented classes the hierarchy is like the following:

  1. Graphics is the superclass
  2. Graphics2D is the subclass of the superclass Graphics

When drawing something in Swing you override the paint() method - And if you're in need of 2D-drawing you downcast the automatically supplied Graphics instance from the paint() method to a Graphics2D instance:

public void paint(Graphics g)
{
    super.paint(g);

    Graphics2D g2d = (Graphics2D)g;
}

But this is reverse? A Graphics instance is NOT inheriting a graphics2D instance - It's the Graphics2D instance that IS inheriting a Graphics instance when looking at the class-hierarchy!

Why is this allowed?

Birdman
  • 5,244
  • 11
  • 44
  • 65
  • If the `Graphics` instance isn't a `Graphics2D`, then this will just throw an exception. – Louis Wasserman Jul 11 '13 at 19:55
  • 2
    Downcasting in general is allowed because sometimes the programmer needs to bypass the type system. This can be due to flaws in the type system or flaws in the API design. In the case of `paint`, I would call that a flaw in the swing API but that is just my opinion. – default.kramer Jul 11 '13 at 20:09

7 Answers7

3

It's allowed if you know that g will indeed be an instance of Graphics2D at runtime. If it is not, you will get a ClassCastException at runtime.

If you check the documentation for Graphics, you will note that there are only two known subclasses of Graphics, Graphics2D and DebugGraphics. So, barring a custom implementation of a Graphics subclass, you can be fairly certain you're dealing with a Graphics2D instance.

GriffeyDog
  • 8,186
  • 3
  • 22
  • 34
  • This isn't what he's asking I don't think. He's asking why do we downcast to a specific instance. Why are things designed this way I think. – William Morrison Jul 11 '13 at 19:43
  • @WilliamMorrison i think he is confused about downcasting, this answer is correct +1 – nachokk Jul 11 '13 at 19:54
  • @nachokk I explain why this downcast is necssary in my answer. Or at least why I think its necessary. I'd love for someone to correct me. This has bothered me since I learned java 2d graphics. – William Morrison Jul 11 '13 at 19:57
  • @WilliamMorrison in the code that show the question is not necesary , only is necesary if you are going to do something and you need specific `Graphics2D` behaviour (methods that don't exist in `Graphics`) – nachokk Jul 11 '13 at 20:02
3

Graphics2D is a class which inherits from Graphics - the same way that "human" is a class of "biped", and it is true that all humans are bipeds but it is not true that all bipeds are humans.

java.awt.Graphics dates to the earliest days of Java and was not very sophisticated. Waphen Swing and Java2D were created, Graphics2D was created as a subclass of Graphics so that old code would work - backwards compatibility was a religion at Sun (and not a bad one).

JComponent and other Swing classes inherit from java.awt.Component, which used the old API. To make it easy for people to migrate from AWT to Swing without making any changes except the superclass, Swing kept the signature of taking a Graphics rather than a Graphics2D.

It is well documented that in Swing, the Graphics passed to paint(Graphics) will always be a Graphics2D, so you do not need an alternate code-path to handle the case that it is not.

Tim Boudreau
  • 1,741
  • 11
  • 13
1

Graphics is a super class of Graphics2D. As for why this cast is necessary, I am not sure. Its something that bothers me too.

Edit:

Check this out:

Graphics2D was introduced after JDK 1.1. I think we need to downcast Graphics to Graphics2D to maintain backwards compatibility with legacy programs.

If you want access to the more advanced functions you cast to Graphics2D. This breaks best practices but was probably deemed worth it to maintain backwards compatibility.

William Morrison
  • 10,953
  • 2
  • 31
  • 48
  • 2
    you downcast cause you are going to use methods from Graphics2D that are not avaiable in `Graphics` – nachokk Jul 11 '13 at 20:01
  • 1
    Duh lol. I am wondering why the java gods took this approach since downcasting to a specific instance is generally undesirable. – William Morrison Jul 11 '13 at 20:11
  • swing is generally undesirable, think about that when you have a `JTextField` it has at least more than 10 supertypes. they play a lot with inheritance so this things happens, also call `super.paint()` is not good at all.. make a template method for that or another thing – nachokk Jul 11 '13 at 20:14
  • @nachokk I don't understand what you're trying to say here... but I think I've answered my own question with my answer. Thanks for your time! – William Morrison Jul 11 '13 at 20:26
1

The object that is passed to the paint method is of the type Graphics2D. You, as a programmer, know this, even though it is not explicit in the method signature (because it need not be true in general). So, it is allowed to cast the object.

Vincent van der Weele
  • 12,927
  • 1
  • 33
  • 61
1

You are confused Graphics is the super class from Graphics2D . You can downcast, but downcasting is dangereous better use instanceof to avoid runtime exception ClassCastException.

Also don't override paint if is not necesary override paintComponent instead.

nachokk
  • 14,363
  • 4
  • 24
  • 53
1

Sometimes downcasting is useful because data is coming from code that cannot possibly know its actual type. Consider this example from the Hibernate Interceptor interface

public void onDelete(Object entity,
                     Serializable id,
                     Object[] state,
                     String[] propertyNames,
                     Type[] types) {
    // do nothing
}

Note that entity is typed as Object even though it is surely a more specific type. Hibernate cannot possibly have a compile-time reference to the types of the entities it is persisting, but this can still be a useful method:

if (entity instanceof Auditable) {
    Auditable data = (Auditable)entity; // downcast
    logger.warn("{current user} is deleting " data.GetDescriptionForAudit());
}

In this example, a cast is unavoidable - the Hibernate API can't be improved, Object is the best type that can be used.

In the case of swing's paint method, it seems that the method signature should have used Graphics2D, or else the methods that you need should have been defined on the Graphics class. Perhaps the designers of swing are trying to say "don't depend on the Graphics2D methods unless you really need to."

To answer the question

Downcasting is allowed because sometimes the programmer has to do it. In the Hibernate example, the API is typed as well as it can be. In the swing example, one might argue that the API has a flaw. But in either case, it is useful that Java allows downcasting.

default.kramer
  • 5,943
  • 2
  • 32
  • 50
0

Actually Graphics2D is subclass of Graphics. When we pass an instance of graphics as an argument say(Graphics g) then internally g is declared like this: g = new Graphics2D. Since Graphics2D is subclass of graphics. Therefore g is now an instance of Graphics 2D with datatype of graphics.Therefore we can downcast g to graphics 2d.

Racil Hilan
  • 24,690
  • 13
  • 50
  • 55
cool joey
  • 167
  • 1
  • 1
  • 7