0

I am trying to write a constructor that creates an instance of a class from an already existing instance of a super class; I am aware of aggregation - I could have created a field of type MyClass - but I do not want to use it. So what I do is take each field in turn and initialize it, like below:

public MyClassExtension extends MyClass
{
    Object new_field;

    public MyClassExtension(MyClass instance) 
    {
        this.field_1 = instance.field_1;
        this.field_2 = instance.field_2;

            ......
        this.field_20= instance.field_20;

        this.new_field= some_value;
    }
}

The problem is that this approach is pretty verbose. Is there a shorter form of achieving the same result?

Attempts:

  • calling super(instance) gives 'the constructor is undefined' error; indeed, there is no such constructor; even if I created it, the same question applies to that constructor

  • calling this(instance) gives 'recursive constructor invocation' error, which is normal, since I am calling a method from within itself with no stop condition

Context information: MyClass is a javax.persistence.Entity, thus the large number of fields. MyClassExtension adds some booleans that store the visibility of UI elements, depending on the data.

Note: I am constrained to use this approach by the technology stack. Prior to EL 2.2 I cannot call a backend method from JSF, to determine the visibility of a UI element. see: How to call a method with a parameter in JSF

EDIT: I have read Copy constructor using reflection, but none of the answers is applicable to me:

  • first answer: I cannot introduce an external library in an existing project without the approval of my manager; I need a core Java functionality

  • second answer: I have already tried super() - see above - I won't duplicate my explanation

  • Possible duplicate of [Copy constructor using reflection](https://stackoverflow.com/questions/21972423/copy-constructor-using-reflection) – AxelH Apr 24 '19 at 09:21
  • The apache beanutils solution would be a good match to your need. I never tried it but this should be exactly what you want. – AxelH Apr 24 '19 at 09:22
  • It's only verbose if you have dozens or hundreds of instance fields, which is already verbose. I've never used or written a so-called 'copy constructor' in Java in 22 years. Why exactly do you think you need one at all? – user207421 Apr 24 '19 at 09:59
  • @user207421 : List of javax Entity is iterated and displayed on a table . Some UI is displayed conditional on the data. So I am wrapping the entity and include booleans related to the visibility of UI elements. I chose this approach because of the technology stack limitations - please read my explanations in the 'Context Information' section of the question – wile the coyote Apr 24 '19 at 10:24
  • Ok, let's admit that 20 lines is not verbose, although I have entities with even more fields. But when copy-pasting, there is always the chance of missing some field, which will then have a default value, and introduce subtle bugs this way. Really, it is not about the effort of copying 20 fields by hand. – wile the coyote Apr 24 '19 at 10:34
  • Why can't you just retain a reference to the original entity in the new object? – user207421 Apr 24 '19 at 10:35
  • In my jsf, currently I call `bean.property`; if I wrap the entity, I would have to call `wrapper.bean.property`, so I will have to replace all the existing occurences in the page with new ones. – wile the coyote Apr 24 '19 at 10:37
  • So you already have all this code working? – user207421 Apr 24 '19 at 10:39
  • The data in my table comes from a list in a backing bean - a list of entities. And I iterate this list using the `var` keyword in JSF. It doesn't really matter if the type of the list changes from `List` to 'List`, because i use the same variable for iteration. However, if i use nested classes, this has to be reflected in the jsf as well – wile the coyote Apr 24 '19 at 10:45
  • Yes, the jsf's are already written. – wile the coyote Apr 24 '19 at 10:48
  • I just realised that the constructor can be generated automatically in eclipse. This saves me from two things: 1. the effort of writing by hand all those fields 2. the chance of skipping any field, and avoid it's default initialisation, that could potentially introduce bugs – wile the coyote Apr 25 '19 at 13:14
  • however, there is still the problem of verbosity – wile the coyote Apr 25 '19 at 13:22

1 Answers1

-2

What you can do is this:

public MyClassExtension(MyClass instance) {
    super(...);
    new_field = some_value;
}

When creating a child class, it is good practice to call the super constructor (otherwise the default super constructor will be called by default).

Why so? Because it limits the number of times you need to redefine the same initialization. Eg when you want to create an object with default values. You only do it once, in the mother class and call by default the default super constructor when creating sub-classes. You may also override default values in sub-classes, depending on how you defined your class.

Here is an example: assuming you have MyClass defined like this:

class MyClass {
    protected Object field1;
    protected Object field2;
    protected Object field3;

    public MyClass(MyClass c) {
        field1 = c.field1; // Remark: this is only the setup of
        field2 = c.field2; // the reference. If you want to create a
        field3 = c.field3; // copy, please do so with a constructor.
    }

    public MyClass(Object field1, Object field2, object field3) {
        this.field1 = field1; // Same remark here
        this.field2 = field2; // (about references).
        this.field3 = field3;
    }
}

You see here that MyClass has 2 constructors defined: a so-called copy constructor that takes a MyClass object as a parameter and a constructor with all the needed fields.

Then, when you want to create a sub-class with a MyClass object as a parameter, you can do this:

class MyClassExtension extends MyClass {
    private Object newField;

    public MyClassExtension(MyClass c, Object newField) {
        super(c);
        this.newField = newField;
    }
}

But you can also do this:

class MyClassExtension {
    private Object newField;

    public MyClassExtension(MyClass c, Object newField) {
        super(c.field1, c.field2, c.field3);
        this.newField = newField;
    }
}

Hence, you limit the number of accesses to the MyClass object parameter and the sub-class MyClassExtension constructor is concise.

Note that you do have to provide this code at some place or the other in your program. You have to define to your compiler how to build a MyClassExtension given a MyClass object. Either the code is in MyClass or both in MyClass and MyClassExtension. I'm only providing a way to reduce the codebase and limit the number of changes needed if you want to change anything in your code. With this solution, if you change the MyClass constructor, MyClassExtension will be affected too (unless you enforce something else).

Also note that you need to define your copy constructors yourself because otherwise the compiler just won't know whether you want to deep copy or simply reference copy. More information here http://blog.amitinside.com/Copy-Constructor-in-Java/

belka
  • 1,480
  • 1
  • 18
  • 31
  • 1
    "_By default you need to call the super constructor when creating a child class._" not really, if a constructor with no parameter (a default constructor works too) is available, it will be called automatically if you don't. – AxelH Apr 24 '19 at 09:18
  • Yep exactly sorry about this imprecision. – belka Apr 24 '19 at 09:54
  • But you understand that the question is about the verbosity of having to copy 20+ variable ? And OPs don't want to associate `MyClassExtension` and `MyClass`, explain the inheritance. – AxelH Apr 24 '19 at 09:56
  • By default, calling the `super()` constructor will already happen, because of compiler action. It isn't clear what exactly you're claiming here for your answer, or what magical properties you think the `super()` constructor has, or how it answers the question that was asked. – user207421 Apr 24 '19 at 10:05
  • @belka: please replace the dots with something concrete such that I can figure out whether your solution is working or not – wile the coyote Apr 24 '19 at 10:26
  • Can you please check and tell me if I'm missing something? Also, if this suits your problem, please accept/upvote the answer. @user207421 Your comments are welcome too. – belka Apr 25 '19 at 12:55
  • At first sight, this appears to move the verbosity from the `MyClassExtension` constructor to `MyClass` constructor. I have already stated that: referring to `MyClass`, " there is no such constructor; even if I created it, the same question applies to that constructor" – wile the coyote Apr 25 '19 at 12:59
  • Even if we call the `MyClass` constructor only once, for setup. The same applies for the subclass constructor - I only intend to call it once. The problem is not where I call it , but where I define it. As I mentioned in other comment, it's not the number of fields (around 20, 30) that is the problem, the problem is the chance that I might miss initialising one of them, by simply skipping it, so that the field will be initialised with default value, and that will be a subtle bug. – wile the coyote Apr 25 '19 at 13:03
  • `MyClassExtension ` does extend `MyClass`, right? I belive so, otherwise you couldn't be calling `super()`; Please don't forget to add that. It is really ok that you avoided agregation, as I specified as a constraint – wile the coyote Apr 25 '19 at 13:18
  • unfortunatelly, this is not the answer I was looking for. I feel sorry for the effort you invested, but before you edited your answer, I had already stated (in fact, from the very beginning I had stated ) that if I call `super()`, this moves the problem of verbosity from the subclass to the superclass. Please take a look at the 'Attempts' paragraph, I have described this situation there. – wile the coyote Apr 25 '19 at 13:27
  • please note that starting from tommorrow I will be on the Easter holiday for a few days and probably I won't login during this time. I appologise for that. – wile the coyote Apr 25 '19 at 13:30
  • Please note that in Java you simply can't create a copy constructor by any other mean than defining it yourself (or with reflection but this is more verbose and I would not advise it). Why so? Because the default `clone` method in `Object` that any object has only copies references of aggregated objects. If you need to `deep` copy (ie create new instances of any contained object and not simply copy the references), you need to define it yourself. I added information in the answer. Please say so if something is missing – belka Apr 26 '19 at 08:39