eclipse-archived/ceylon

Add option to not generate noarg constructors

Closed this issue · 24 comments

I already had the problem with jOOQ, and now I have it with NetBeans' annotation processors. Sometimes tools or frameworks don't work properly because they use the implicit @Jpa noarg constructor instead of the constructors I define in my classes.

I would like the possibility to opt-out of these implicit constructors, either via a compiler option (that applies to the whole project), or via an annotation to target a single class.

The current workaround is to add a Java superclass with an explicit constructor that takes a dummy argument (eeek!).

Possible names for the annotation:

  • noNoArgConstructor
  • noImplicitConstructor
  • iDontCareAboutJpaAndThisConstructorIsUselessMostOfTheTimes

But this wasn't done primarily for JPA. This was done because it's a requirement of Java serialization.

Frankly I think the problem here is NetBeans annotation processors being shit, not anything wrong with Ceylon.

OK, but still there are cases where I don't need serialization, and where I don't want that constructor.

You'll have to deal with it, there are a lot of "shitty tools" around.

I'm completely against adding an annotation to Ceylon for this, frankly.

Default constructors are even a requirement of the JavaBeans spec, FTR. So there's plenty of good reasons to generate them. It's nothing to do with JPA per se.

Except all classes are not JavaBeans!

What we could do is add an abstract class named Unserializable written in Java to ceylon.interop.java. That would at least solve the problem for cases where you don't need to extend another class.

That's another possibility, but AFAIU it would still need a dummy parameter, otherwise the subclass would be able to call super() and the noarg constructor would still be generated.

I believe it would look like this:

public abstract class Unserializable {
    public Unserializable(String unused) {}
}
class MyClass(MyParam param) extends Unserializable("") {
}

I think we might be able to make it work by giving it something that looked to Ceylon like a named constructor. Would have to try that out though.

Then you would write:

class MyClass(MyParam param) extends Unserializable.create() {}

This Unserializable is horrible because it doesn't work in the case where you need to extend some other class.

Why can't we have an annotation in ceylon.java.interop?

Actually what about declaring a non-shared default constructor?

@tombentley well the one we generate is already protected, so apparently NetBeans goes to great lengths to call the wrong constructor.

But would a private one work, for NetBeans and for JOOQ? Because we've already got #6611

It works with this class:

@Ceylon(major = 8)
@Class(constructors=true, extendsType="ceylon.language::Basic")
public class Unserialized {

    @Ignore
    public static final Unserialized.create_ create_ = null;

    @Ceylon(major = 8)
    @Ignore
    @ConstructorName("create")
    public static final class create_ {
        private create_() {
        }
    }

    @Name("create")
    public Unserialized(@Ignore Unserialized.create_ $name$) {
    }
}

I could live with that.

This Unserializable is horrible because it doesn't work in the case where you need to extend some other class.

You wouldn't need it if the superclass has no noarg constructor. I agree this specific case can still happen, and we have no solution for that yet.

@quintesse given this class (it's Ceylon's native):

class Native(s, shared native String u) {
    shared native String s;
    shared native String t;
    shared native String m();
}

It seems we generate a getter for u, and fields for u and s, but nothing else. I don't really understand why, but I need to if we're going to support javaNative native declarations. Can you enlighten me?

Grrr wrong issue

I guess we could add an annotation to java.lang, similar to what I've done with overloaded.

I guess we could add an annotation to java.lang, similar to what I've done with overloaded.

I'd like to implement this change. From what I understand, here's what we need:

  • a new annotation named unserialized (or unserializable?)
  • that can only appear on class declarations
  • which will make the JVM backend skip noarg constructors
shared unserializable class MyClass(String arg) {
}

@gavinking wdyt?

Sure, go ahead, it should not be very hard at all.

I added that annotation in a branch and it works as expected, but I don't really like its name. It's job is not to skip the serialization stuff (which is enabled by serializable), it's only skipping the JPA noargs constructor. Does someone have a better name?

unbeany :-)

xkr47 commented

nonbean

nonbean is acceptable, I guess. Closing :)

Thanks Bastien.