1

I have a very complex class AComplex which could contain tons of private members, now I'd like to make a public version of the class (call it : ASimple_V1) which only contains a subset of private members, and made them public. Also later I'd like to make more versions based on the previous version (e.g., ASimple_V2, based on ASimple_V1 but added more from AComplex).. the current solution I had is really clumsy:

class AComplex{
    private T1 t1;
    private T2 t2;
    ....
}

class ASimple_V1{
    public T1 t1;
    public T2 t2;
}

class ASimple_V2 extends ASimple_V1{
    public T3 t3;
    public ASimple_V2(AComplex a){
        t1 = a.t1;
        t2 = a.t2;
        t3 = a.t3;
    }
}

I use a constructor which takes the complex class as param, still need to manually assign all values. Are there any better ways to achieve such goal?

sbswim
  • 276
  • 2
  • 15

4 Answers4

0

If your question is about avoiding manual copying of the properties, have a look at BeanUtils from Apache commons. You will have to match the names of source and destination beans and your classes will have to confirm to Javabeans conventions.

In ASimple_V2, what you have is just a reference to the "complex object". Creating a "simpler copy" of the complex class like you have done is not a standard practice. You could also change a number of other things to improve the design of your classes. In general, it is not a good OO practice to have public fields. What you have in your complex class are probably getters for the private fields.

senseiwu
  • 5,001
  • 5
  • 26
  • 47
0

you could solve your problem with reflection this.getClass().getDeclaredFields() but i think it would be better to restructure your code.

see the comments of roufamatic in the accepted answer of this questions: Java reflection: Find fields of a subclass

Community
  • 1
  • 1
rmacnell
  • 11
  • 4
0

First of all, private fields usually contain implementation-specific details and should remain hidden. If they remain hidden, you have the flexibility to change the way your class is implemented without affecting the API. By exposing them publicly, they become a part of your exportable API, and you are stuck with them---forever.

The first question you should be asking yourself is, "Why can these fields NOT remain private?" If there is a compelling reason, you still should carefully consider whether or not to just expose them publicly; often, it's still a bad idea (as a negative example, look at the java.awt.Point class). Instead of making the fields public, you should provide accessor methods, e.g. getT1 and getT2. This will allow you more flexibility to change the implementation later.

Finally, as you build-out your class hierarchy, how do plan to maintain the equals/hashCode contracts between versions---specifically, the reflexive relationship (as in, v1.equals(v2)==true but v2.equals(v1)==false)?

My recommendation: Keep as many fields private as possible. If you must expose a field, do so via a public getter method. Eclipse has an automated tool to generate getters and setters for you.

Austin
  • 8,018
  • 2
  • 31
  • 37
0

If you just want to reduce the amount of copy code you have to write, you can use reflection to copy the members with the same names, see Copy fields between similar classes in java - Stack Overflow.

However, if you want provide other coders with different abstractions without the overhead of copying, you can maintain a single package level implementation class that implements multiple public interfaces, which provide different degrees of access to the same object created by this class. Besides, provide necessary factory methods and conversion utility functions. Code outside the package can manipulate objects of these interfaces instead of that class and perform necessary conversion.

See the code below:

public interface AComplex {
    T1 getT1();
    T2 getT2();
    T3 getT3();
    // ...
}
public interface ASimpleV1 {
    T1 getT1();
    T2 getT2();
}
public interface ASimpleV2 extends ASimpleV1 {
    T3 getT3();
}
class AImpl implements AComplex, ASimpleV1, ASimpleV2 {
    T1 t1;
    T2 t2;
    T3 t3;

    @Override
    public T1 getT1() {
        return t1;
    }

    @Override
    public T2 getT2() {
        return t2;
    }

    @Override
    public T3 getT3() {
        return t3;
    }

    // ...
}

public final class As {
    private As() {
    }

    public static ASimpleV1 newASimpleV1() {
        return new AImpl();
    }

    public static ASimpleV1 newASimpleV1(T1 t1, T2 t2) {
        return new AImpl(t1, t2, null);
    }

    // ...

    public static ASimpleV2 ASimpleV1AsASimpleV2(ASimpleV1 aSimpleV1) {
        return (ASimpleV2) aSimpleV1;
    }

    public static ASimpleV2 ASimpleV1AsASimpleV2(ASimpleV1 aSimpleV1, T3 t3) {
        AImpl aImpl = (AImpl) aSimpleV1;
        aImpl.t3 = t3;
        return (ASimpleV2) aImpl;
    }

    public static ASimpleV1 ASimpleV2AsASimpleV1(ASimpleV1 aSimpleV2) {
        return (ASimpleV1) aSimpleV2;
    }

    // ...
}

Note that AImpl and its member variables are of package level visibility, so code outside the package can not operate on its members directly, except using reflection of course.

For simplicity I have only written getters, you can also add setters and other methods as needed.

Shreck Ye
  • 1,591
  • 2
  • 16
  • 32