# Friday, November 4, 2011
« New Development Snapshot | Main | Java Method Overriding Is FUBAR Part 2 o... »
Java Method Overriding Is FUBAR Part 1 of ∞

The Java Virtual Machine Specification Java SE 7 Edition finally has a section about method overriding (5.4.5). Unfortunately, it does not document the behavior of the Java 7 reference implementation, nor the Java 6 behavior. It also turns out to be a bad design.

Suppose you have a third party widget framework that has a class:

package org.example;

public class Widget

And your application has a subclass of Widget:

package fubar.tech;

public class MyWidget extends org.example.Widget
  void frob() { ... }

The frob() method is package private and you don't want external code calling it. Now a new version of the widget framework is released that adds a frob method to Widget:

package org.example;

public class Widget
  public void frob() { }

Starting with Java 7 (and this particular behavior is defined by the spec) your package private MyWidget frob() will now override Widget.frob() and hence be callable by anyone who happens to have a reference to your object. If MyWidget.frob() does anything security critical, the third party framework has now introduced a vulnerability in your code.

Java already had a poor story for adding virtual methods to public classes, but this change has made it even worse.

Friday, November 4, 2011 7:01:39 AM (W. Europe Standard Time, UTC+01:00)  #    Comments [7]
Friday, November 4, 2011 6:34:30 PM (W. Europe Standard Time, UTC+01:00)
Would your criticism be satisfied If the compiler were changed to

(1) Mangle all method names and fields with the visibility level
(2) When an override raises visibility, add a forwarding method
(3) Possibly add unmangled forwarding methods for public methods only for backwards compatibility with older class file versions.

For example, (and ignoring identifier hygiene for simplicity), when the java source file below is compiled:

class C {
void foo() {}
protected void bar() {}

class D extends C {
public foo() {}
@Override public bar() {}

the .class file for C would define

package$foo() // mangled per (1)

and the .class file for D would define

// synthetic forwarder
void protected$bar() { public$bar(); } // per (2)
void foo() { public$foo(); } // per (3)
void bar() { public$bar(); } // per (3)

Static methods would not change, so there would be no need to change how main is located. Class.getMethod and friends would need to change to prefer the most-visible version, but would not change in API.
Mike Samuel
Friday, November 4, 2011 9:21:27 PM (W. Europe Standard Time, UTC+01:00)
I just tried your exact example on my machine with Java 7, and the compiler generated the following error message:

Cannot reduce the visibility of the inherited method from Widget

So I'm confused. Did you try this yourself and get a different result?
Friday, November 4, 2011 10:37:02 PM (W. Europe Standard Time, UTC+01:00)
Confirmed above, OpenJDK7b147 produces a compile time error.
Friday, November 4, 2011 10:43:29 PM (W. Europe Standard Time, UTC+01:00)
I would expect an IncompatibleClassChangeError in this case. What IKVM do in this case?
Friday, November 4, 2011 11:32:42 PM (W. Europe Standard Time, UTC+01:00)
Guys, I'm a JVM developer I don't care about the javac compiler only about the runtime. It doesn't matter one bit what javac does, situations like this can and do arise in real life and need to be (better) supported by the VM.
Friday, November 4, 2011 11:35:07 PM (W. Europe Standard Time, UTC+01:00)
@Volker After I check in my current changes IKVM will do the same as HotSpot (apart from the exception described in part 5).
Friday, November 4, 2011 11:37:44 PM (W. Europe Standard Time, UTC+01:00)
@Mike Samuel It would be possible to add a lot of hacks to work around some of these issues, but the main point of these posts is to question what Oracle have been doing, because the Java 6 behavior, while certainly not perfect, was a lot less broken than the new behavior.
Comments are closed.