Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep out of reach of Children: Removing protected fields from inheritance

In the spirit of well designed OO, a certain class I am extending has marked one of its fields protected. This class has also generously provided a public setter, yet no getter.

I am extending this class with a base class that is in turn extended by several children. How can I restrict access to the protected variable from my children while still being able to manipulate it privately and set it publicly?

See example below:

public abstract class ThirdPartyClass {
  protected Map propertyMap;

  public void setPropertyMap(Map propertyMap){
    this.propertyMap= propertyMap;
  }

  // Other methods that use propertyMap.
}

public abstract class MyBaseClass extends ThirdPartyClass{
// Accessor methods for entries in propertyMap.
  public getFoo(){
    propertyMap.get("Foo");
  }

  public getBar(){
    propertyMap.get("Bar");
  }

 // etc...
}

public class OneOfManyChildren extends MyBaseClass {
// Should only access propertyMap via methods in MyBaseClass.
}

I have already found that I can revoke access by making the field private final in MyBaseClass. However that also hinders using the setter provided by the super class.

I am able to circumvent that limitation with the "cleverness" below yet it also results in maintaining two copies of the same map as well as an O(n) operation to copy over every element.

public abstract class MyBaseClass extends ThirdPartyClass{

  private final Map propertyMap = new HashMap(); // Revokes access for children.

  /** Sets parent & grandparent maps. */
  @Override
  public final void setPropertyMap(Map propertyMap){
    super.setPropertyMap(propertyMap);
    this.propertyMap.clear();
    this.propertyMap.putAll(propertyMap);
  }
}

Are there any better ways of accomplishing this?

Note: This is only one example of the real question: How to restrict access to protected fields without maintaining multiple copies?

Note: I also know that if the field were made private in the first place with a protected accessor, this would be a non-issue. Sadly I have no control over that.

Note: IS-A relatonship (inheritance) required.

Note: This could easily apply to any Collection, DTO, or complex object.

Metaphor for those misunderstanding the question:

This is akin to a grandparent having a cookie jar that they leave accessible to all family members and anyone else in their house (protected). A parent, with young children, enters the house and, for reasons of their own, wishes to prevent their children from digging into the cookie jar ad nauseam. Instead, the child should ask the parent for a chocolate chip cookie and see it magically appear; likewise for a sugar cookie or Oreo. They need never know that the cookies are all stored in the same jar or if there even is a jar (black box). This could be easily accomplished if the jar belonged to the parent, if the grandparent could be convinced to put away the cookies, or if the grandparents themselves did not need access. Short of creating and maintaining two identical jars, how can access be restricted for children yet unimpeded for the parent & grandparent?

like image 557
AnthonyW Avatar asked Jan 31 '14 14:01

AnthonyW


People also ask

Can child class access protected variables?

While protected members can be accessed anywhere in the same package and outside package only in its child class and using the child class's reference variable only, not on the reference variable of the parent class. We can't access protected members using the parent class's reference.

Will all properties of a parent can acquire by child class in inheritance?

I think No. Inheritance concept is to inherit properties from one class to another but not vice versa. But since parent class reference variable points to sub class objects. So it is possible to access child class properties by parent class object if only the down casting is allowed or possible....

How is inheritance defined in Java?

Inheritance is a mechanism wherein a new class is derived from an existing class. In Java, classes may inherit or acquire the properties and methods of other classes. A class derived from another class is called a subclass, whereas the class from which a subclass is derived is called a superclass.


2 Answers

This might not be possible for you, but if you could derive an interface from ThirdPartyClass and make ThirdPartyClass implement it ?

Then have MyBaseClass act as a decorator by implementing the interface by delegating to a private member ThirdPartyClassImpl.

I.e.

public interface ThirdParty ...


public class ThirdPartyClass implements ThirdParty



public class MyBaseClass implements ThirdParty {

    private ThirdParty decorated = new ThirdPartyClass();



 public class SubclassOne extends MyBaseClass....

etc

like image 79
Woody Avatar answered Sep 22 '22 03:09

Woody


Ok, cheating mode on: How about you overwrite de public setter and change the map implementation to a inner class of MyBaseClass. This implementation could throw a exception on all methods of map you dont want your children to access and your MyBaseClass could expose the methods they should use by using an internal method your map implementation... Still has to solve how the ThirdPartyMethod will access those properties, but you could force your code to call a finalizationMethod on your MyBaseClass before use it... I'm just divagating here

EDIT

Like This:

public abstract class MyBaseClass extends ThirdPartyClass{

    private class InnerMapImpl implements Map{
       ... Throw exception for all Map methods you dont want children to use

       private Object internalGet(K key){
           return delegate.get(key);
       }
    }

    public void setPropertyMap(Map propertyMap){
        this.propertyMap= new InnerMapImpl(propertyMap);
    }

    public Object getFoo(){
        return ((InnerMapImpl) propertyMap).internalGet("Foo");
    }


}
like image 28
Plínio Pantaleão Avatar answered Sep 21 '22 03:09

Plínio Pantaleão