Java objects: back from the dead

Challenge: can you write code that invokes foo()?
class Impossible {
  public Impossible() {
    throw new AssertionError("you cannot instantiate me");
  }

  public void foo() {
    System.out.println("The impossible is possible!");
  }
}

The solution is in the comments...

9 comments:

swankjesse said...

Unfortunately you can! It takes a gross combination of subclasses and a badly behaved finalizer:

class Defy extends Impossible {
  static Defy STATIC_INSTANCE = null;

  @Override protected void finalize() {
    STATIC_INSTANCE = this;
  }

  public static void main(String... args) {
    try {
      new Defy();
    } catch (Error e) {
    }

    for (int i = 0; i < 10; i++) {
      System.gc();
      if (Defy.STATIC_INSTANCE != null) {
        Defy.STATIC_INSTANCE.foo();
        break;
      }
    }
  }
}

Even dead-on-arrival objects whose constructors fail will still be finalized. You should be careful about using constructors to enforce security!

Xavier said...

indeed, that's why I always strongly advise against registering objects to other objects in their constructor like:

class MyObject {

  public MyObject(Registry registry) {
    registry.register(this);
    ...
    throw SomethingHere;
  }
}

Dan Lewis said...
This comment has been removed by the author.
Dan Lewis said...

this works too:
ChuckNorris.newInstance(Defy.class).foo();

jessewilson said...

Update: this was fixed in Java 6!

For embarrassing reasons, I'm usually running a Java 5 VM, but hopefully that will change Real Soon Now.

samskivert said...

Actually, the code you posted still works. What was "fixed" was that if Object's constructor is never run, the finalizer won't be called. But in your example Object's constructor is run.

This object won't be finalized:

class A {
public A (int value) {}
}

class B extends A {
public B () {
super(fail());
}
static int fail () {
throw new RuntimeException();
}
protected void finalize () {
System.out.println("Woe is me. I'm never called.");
}
}

Sam Berlin said...

A sometimes-possible workaround would be to declare the constructor private & use factory creation methods. (Only possible if you can disable subclassing, which, if you're using exceptions in the constructor for security, is a reasonable assumption.)

Jan-Kees said...

You can also bypass the constructor completely, like shown here:
http://www.javaspecialists.eu/archive/Issue175.html

ReflectionFactory rf = ReflectionFactory.getReflectionFactory();
Constructor objDef = parent.getDeclaredConstructor();
Constructor intConstr = rf.newConstructorForSerialization(clazz, objDef);
return clazz.cast(intConstr.newInstance());

rubyorchard said...

sun.misc.Unsafe can be used to by-pass constructor (it may not work in all JDKs):

Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe u = (Unsafe) unsafeField.get(null);
Impossible i = (Impossible) u.allocateInstance(Impossible.class);
i.foo();