8

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

Neuron
  • 5,141
  • 5
  • 38
  • 59
tatsuhirosatou
  • 25,149
  • 14
  • 39
  • 40
  • 1
    I think you shold be clear what is the base and super here. The answers seem confused. It would be reasonable to have the restrictedUser the base class since it can do less. Intuitively though, you'll get into trouble this way since in English "RestrictedUser" sounds more Specialized than just User. – Hugo Apr 03 '09 at 12:15
  • What are you trying to achieve anyway? Why do you want to "hide" the subclass methods? – Michael Borgwardt Apr 03 '09 at 13:05

10 Answers10

17

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
  • 35,947
  • 7
  • 94
  • 101
3

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
  • 2,350
  • 8
  • 26
  • 46
3

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
  • 34,554
  • 11
  • 80
  • 120
3

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
[]]

user85421
  • 28,957
  • 10
  • 64
  • 87
2

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.

Community
  • 1
  • 1
C. K. Young
  • 219,335
  • 46
  • 382
  • 435
2

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
  • 4,004
  • 1
  • 31
  • 33
  • 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 Apr 03 '09 at 12:34
  • Yes, it might even make sence to override the implementation of "restricted" method with a securityException – Hugo Apr 03 '09 at 12:51
  • Thanks Hugo for the naming tip :) – tatsuhirosatou Apr 03 '09 at 13:54
1

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.)

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 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 Apr 03 '09 at 10:43
  • 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! – C. K. Young Apr 03 '09 at 13:53
  • 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. – C. K. Young Apr 03 '09 at 14:17
0

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
  • 2,050
  • 13
  • 15
0

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
  • 19,122
  • 11
  • 62
  • 71
0

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
  • 26,349
  • 9
  • 53
  • 79