views:

995

answers:

10

On the program I'm writing I have a class RestrictedUser and class User that is derived from RestrictedUser. I'm trying to hide the User specific methods by casting to RestrictedUser but when I do the casting the User methods are still available. Also when I run the debugger the type of the variable comes up as User.

RestrictedUser restricted = regularUser;

Does up casting in Java hide the subclass methods and fields or am I doing something wrong? Is there a workaround?

Thanks

+3  A: 

I'm not sure exactly what you are asking as the terminology you are using is a little unclear but here goes. If you have a superclass and a subclass you can hide the methods in the superclass from the subclass by making them private. If you need public methods on the superclass to be invisible you are out of luck. If you cast the subclass to the superclass then the public methods on the subclass are no longer visible.

One suggestion that might help you would be to use composition rather than inheritance, extract the sensitive methods into a separate class and then insert an appropriate instance of that class into the object as needed. You can delegate methods from the class to the injected class if you need to.

Peter Kelley
+12  A: 

If you were to attempt to run this code:

User user = new User(...);
RestrictedUser restricted = user;
restricted.methodDefinedInTheUserClass(); // <--

you would get a compilation error. That won't be secure in any way, shape, or form, because even if you were to pass around the RestrictedUser to, say, another method, that method could do this:

if (restricted instanceof User) {
    User realUser = (User)restricted;
    realUser.methodDefinedInTheUserClass(); // !!
}

and your "restricted" user isn't so restricted anymore.

The object is showing up in the debugger as a User object because it is as User object, even if the object reference is stored in a RestrictedUser variable. Basically, putting an instance of a child class in a variable with a parent class's type will indeed "hide" the subclass's methods and fields, but not securely.

John Calsbeek
+2  A: 

You're confusing static type and dynamic type.

Static type is the type of the reference. When you up-cast from Derived to Base, you're telling the compiler that as far as you and it know, the thing pointed to is a Base. That is, you're promising that the object pointed to is either null, or a Base, or something derived from Base. Which is to say, it has Base's public interface.

You won't then be able to call methods declared in Derived (and not in Base), but when you call any Base methods overridden by Derived, you'll get Derived's overridden version.

tpdi
+2  A: 

Peter's answer and John's answer are correct: just casting the static type will do nothing if the real type of the object (which client code can cast to, call reflection methods on, etc.) is still available. You have to mask the real type somehow (as Peter's answer mentions).

There is actually a pattern for doing this: have a look at the unconfigurable* methods in the Executors class, if you have access to the JDK source code.

Chris Jester-Young
+1  A: 

Also, don't be tempted to think that you can hide methods in an anonymous class either. For example, this won't provide the privacy you expect:

Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }

    public void secretSquirrel() {
        System.out.println("Surprise!");
    }
}

You may not be able to name the real type of run (in order to cast to it directly), but you can still get its class with getClass(), and you can call secretSquirrel using reflection. (Even if secretSquirrel is private, if you have no SecurityManager, or if it's been set up to allow accessibility, reflection can invoke private methods too.)

Chris Jester-Young
Assuming you have untrusted code (and it's in a different package!), then you shouldn't be able to invoke the secretSquirrel method, because the (anonymous inner) class is package private as far as the runtime is concerned.
Tom Hawtin - tackline
True, that's a good point (just wrote a test to test this too), if the package is different then accessibility is required too. Thanks!
Chris Jester-Young
For lurkers and archives: "accessibility is required" means it's as secure as Java can make it. With accessibility, you can break encapsulation whatever way you want, e.g., access private fields, etc. That implies that delegate pattern (or anything else) is defenceless against it.
Chris Jester-Young
+3  A: 

If you need to protect the subclass, you can use the Delegate pattern:

public class Protect extends Superclass {  // better implement an interface here
  private final Subclass subclass;

  public Protect(Subclass subclass) {
    this.subclass = subclass;
  }

  public void openMethod1(args) {
    this.subclass.openMethod1(args);
  }

  public int openMethod2(args) {
    return this.subclass.openMethod2(args);
  }
  ...
}

You can also think about using java.lang.reflect.Proxy
[]]

Carlos Heuberger
Why the downvote (after 8 months)?
Carlos Heuberger
+2  A: 

Java

In Java, you can never "Hide" something from an object. The compiler can forget what you know in detail about the particular instance you have. (like what subclass it is) The debugger knows much more than the compiler, so it will tell you what actual type the current instance is, which is probably what you're experiencing.

OOP

It sounds like you're writing logic that needs to know what type of object you're using which is not recommended in OOP but often required for practical reasons.

Restricting Adjectives

As I said in a comment to the question, you should be clear on what is the base class here. Intuitively RestrictedUser should be a subclass of User since the name suggests a more specialized type. (Name having an additional Active Adjective on it.) In your case this is special since this is a limiting adjective which would let you put User as a subclass of RestrictedUser totally fine. I would recommend renaming the two to something like: BasicUser/UserWithId and NonRestrictedUser to avoid the limiting adjective which is the confusing part here.

Hugo
I'd advise this change as well - the base class should have the basic functions that ALL users need, and the subclasses should extend and add to those - OOP doesn't seem to have been designed so support subclasses not being able to do what their parent classes can do.
Knobloch
Yes, it might even make sence to override the implementation of "restricted" method with a securityException
Hugo
Thanks Hugo for the naming tip :)
climatewarrior
A: 

I think you probably made a poor naming choice: I would think RestrictedUser would be a subclass of User, not the other way around, so I'll use UnrestrictedUser as the subclass.

If you upcast an UnrestrictedUser to a RestrictedUser, your reference will only be able to access methods declared in RestrictedUser. However, if you downcast it back to an UnrestrictedUser, you'll have access to all of the methods. Since Java objects know what type they really are, anything that actually is an UnrestrictedUser can always be cast back to it, no matter what type of reference you are using (even an Object). There is unavoidable and part of the language. However, you could hide it behind some sort of proxy, but in your case that probably defeats the purpose.

Also, in Java, all non-static methods are virtual by default. This means that if UnrestrictedUser overrides some method declared in RestrictedUser, then the UnrestrictedUser version will be used, even if you access it through a RestrictedUser reference. If you need to invoke the parent class version, you can make a non-virtual call by calling RestrictedUser.variableName.someMethod(). However, you probably shouldn't use this very often (the least of the reasons being that it breaks encapsulation).

James
A: 

I also think this question begs another question. How do you plan to call this method? It seems like having a Class heirarchy where subclassing introduces new method signatures will require instanceof checks in callers.

Can you update your question to include a scenario where you would call these methods in question? Perhaps we will be able to help more.

Nathan Feger
A: 

The technical answer has been given by John Calsbeek. I want to think about the design issue. What you are trying to do is prevent some segment of the code, or some developers, from knowing anything about the User class. You want them to treat all Users as if they were RestrictedUsers.

The way you would do that is with permissions. You need to prevent the developers in question having access to the User class. You can probably do that by placing it in a package and restricting access to the package.

DJClayworth