views:

581

answers:

9

So, in Java, the first line of your constructor HAS to be a call to super... be it implicitly calling super(), or explicitly calling another constructor. What I want to know is, why can't I put a try block around that?

My specific case is that I have a mock class for a test. There is no default constructor, but I want one to make the tests simpler to read. I also want to wrap the exceptions thrown from the constructor into a RuntimeException.

So, what I want to do is effectively this:

public class MyClassMock extends MyClass {
    public MyClassMock() {
        try {
            super(0);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    // Mocked methods
}

But Java complains that super isn't the first statement.

My workaround:

public class MyClassMock extends MyClass {
    public static MyClassMock construct() {
        try {
            return new MyClassMock();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public MyClassMock() throws Exception {
        super(0);
    }

    // Mocked methods
}

Is this the best workaround? Why doesn't Java let me do the former?

A: 

My best guess as to the "why" is that Java doesn't want to let me have a constructed object in a potentially inconsistent state... however, in doing a mock, I don't care about that. It seems I should be able to do the above... or at least I know that the above is safe for my case... or seems as though it should be anyways.

I am overriding any methods I use from the tested class, so there is no risk that I am using uninitialized variables.

Mike Stone
+1  A: 

Unfortunately, what you are trying to do is illegal in java.

Have you considered using something like EasyMock instead of rolling your own?

EasyMock can use reflection to create mocks for you. It has a great class extension so you can mock concrete classes as well as interfaces (even doing a "partial" mock of a concrete class maintaining some of its functionality).

Going a step further, check out the unitils package which provides a nifty wrapper around EasyMock that makes mocking even simpler.

Justin Standard
A: 

@Justin Standard:

Thanks for the response, but I'm really more interested in understanding why I can't use a try block in the constructor around a call to super. I've never had trouble rolling my own mocks (they are ridiculously easy to create, especially with things like Eclipse which will auto generate abstract methods and interface methods for you). Anyways that was just details giving context for what I'm really interested in figuring out.

Mike Stone
+5  A: 

Unfortunately, compilers can't work on theoretical principles, and even though you may know that it is safe in your case, if they allowed it, it would have to be safe for all cases.

In other words, the compiler isn't stopping just you, it's stopping everyone, including all those that don't know that it is unsafe and needs special handling. There are probably other reasons for this as well, as all languages usually have ways to do unsafe things if one knows how to deal with them.

In C# .NET there are similar provisions, and the only way to declare a constructor that calls a base constructor is this:

public ClassName(...) : base(...)

in doing so, the base constructor will be called before the body of the constructor, and you cannot change this order.

Lasse V. Karlsen
A: 

@lassevk: That's along the lines of what I was thinking was the case, so unless someone else has a better reason, thanks for the answer!

Mike Stone
+1  A: 

I don't know how Java is implemented inside but if the contructor of the superclass throws an exception, then there isn't a instance of the class you extend, so that... how could you call the... toString(), equals()... methods which are suposed to be inherited in most cases.

Java may allow the try{}catch{} around the super() call in the contructor if you override ALL methods from the superclasses, and you dont use the super.XXX() clause, but it's sound too complicated to me.

Telcontar
+1  A: 

I can't presume to have a deep understanding of Java internals, but it is my understanding that, when a compiler needs to instantiate a derived class, it has to first create the base (and its base before that(...)) and then slap on the extensions made in the subclass.

So it is not even the danger of uninited variables or anything like that at all. When you try to do something in the subclass' constructor before the base class' constructor, you are basically asking the compiler to extend a base object instance that doesn't exist yet.

Edit:In your case, MyClass becomes the base object, and MyClassMock is a subclass.

Ishmaeel
A: 

@Telcontar & Ishmaeel:

I don't know the internals of how the language is implemented, but there is no base object... there is just 1 object. The base object is the same object as the extended version when you construct the extended version. If you are constructing the extended object, the compiler knows precisely how much memory it needs to allocate etc, the constructor just deals with initialization, so unless I have a completely mixed view of how the language is implemented, the only issue really is whether the base class properly initialized its state or not.

Mike Stone
A: 

It's done to prevent someone from creating a new SecurityManager object from untrusted code.

public class Evil : SecurityManager { Evil() { try { super(); } catch { Throwable t } { } } }

Joshua