Java Name Hiding: The Hard Way
Asked Answered
S

9

97

I have a problem with name hiding that is extremely hard to solve. Here is a simplified version that explains the problem:

There is a class: org.A

package org;
public class A{
     public class X{...}
     ...
     protected int net;
}

Then there is a class net.foo.X

package net.foo;
public class X{
     public static void doSomething();
}

And now, here is the problematic class which inherits from A and wants to call net.foo.X.doSomething()

package com.bar;
class B extends A {

    public void doSomething(){
        net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
        X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
    }
}

As you see, this is not possible. I cannot use the simple name X because it is hidden by an inherited type. I cannot use the fully qualified name net.foo.X, because net is hidden by an inherited field.

Only the class B is in my code base; the classes net.foo.X and org.A are library classes, so I cannot alter them!

My only solution looks like this: I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messy! Is there no solution in which I can directly call X.doSomething() from B.doSomething()?

In a language that allows specifying the global namespace, e.g., global:: in C# or :: in C++, I could simply prefix net with this global prefix, but Java does not allow that.

Strangury answered 4/7, 2014 at 10:25 Comment(5)
Some quickfix could be this: public void help(net.foo.X x) { x.doSomething(); } and call with help(null);Head
@Absurd-Mind: Well, calling a static method via a non existent object seems even messier than my solution :). I will even receive a compiler warning. But true, this would be another hacky solution in addition to mine.Strangury
@JamesB: This would instanciate the wrong X! net.foo.X has the method, not org.A.X!Strangury
Do you have to inherit from A? Inheritance can be so nasty, as you've found…Trinette
I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messy +1 for attitude of clean code. But for me it seems like this is a situation you should do a tradeoff. Simply do this, and throw a nice long comment about why you had to do it (probably with a link to this question).Masefield
T
85

You can cast a null to the type and then invoke the method on that (which will work, since the target object isn't involved in invocation of static methods).

((net.foo.X) null).doSomething();

This has the benefits of

  • being side-effect free (a problem with instantiating net.foo.X),
  • not requiring renaming of anything (so you can give the method in B the name you want it to have; that's why a import static won't work in your exact case),
  • not requiring the introduction of delegate class (though that might be a good idea…), and
  • not requiring the overhead or complexity of working with the reflection API.

The downside is that this code is really horrible! For me, it generates a warning, and that's a good thing in general. But since it's working around a problem that is otherwise thoroughly impractical, adding a

@SuppressWarnings("static-access")

at an appropriate (minimal!) enclosing point will shut the compiler up.

Trinette answered 4/7, 2014 at 13:14 Comment(5)
Why not? You'd just do the right thing and refactor the code.Rainbolt
Static imports aren't that much clearer than this solution and don't work in all cases (you're screwed if there's a doSomething method anywhere in your hierarchy), so yep best solution.Maltreat
@Maltreat I freely admit that I actually went and tried all the options that other people had listed as answers having first reproduced exactly what the original problem was, and I was startled how hard it was to work around. The original question neatly closes off all the other, nicer options.Trinette
Vote up for creativity, I would never do this in production code though. I believe indirection is the answer to this problem (isn't it for all problems ?).Choir
Thanks Donal for this solution. Actually, this solution highlights the places where a "Type" can be used and a variable cannot be used. So, in a "cast" , the compiler would choose the "Type" even if there is a variable with same name. For more such interesting cases, I would recommend 2 "Java Pitfalls" books: books.google.co.in/books/about/… books.google.co.in/books/about/…Carthusian
B
38

Probably the simplest (not necessarily the easiest) way to manage this would be with a delegate class:

import net.foo.X;
class C {
    static void doSomething() {
         X.doSomething();
    }
}

and then ...

class B extends A {
    void doX(){
        C.doSomething();
    }
}

This is somewhat verbose, but very flexible - you can get it to behave any way you want; plus it works in much the same way both with static methods and instantiated objects

More about delegate objects here: http://en.wikipedia.org/wiki/Delegation_pattern

Bromberg answered 4/7, 2014 at 11:9 Comment(1)
Probably the cleanest solution provided. I would probably use that if I had that problem.Lashawna
H
37

You can use a static import:

import static net.foo.X.doSomething;

class B extends A {
    void doX(){
        doSomething();
    }
}

Watch out that B and A do not contain methods named doSomething

Head answered 4/7, 2014 at 10:43 Comment(6)
Doesn't net.foo.X.doSomething have package access only? Meaning you cannot access it from package com.barHelios
@Helios Yes, but then the complete question wouldn't make sense, since the method wouldn't be accessible in any way. I think this is an error while simplifying the exampleHead
@JamesB: My mistake; it is public. Changed it!Strangury
But the original question seems to actively want to use doSomething as the name of the method in BTrinette
@DonalFellows: You are right; this solution does not work if my code had to remain as I posted. Fortunately, I can rename the method. However, think of a case where my method overrides another method, then I cannot rename it. In this case, this answer would not solve the problem indeed. But this would be even more coincidentally than my problem already is, so maybe there will never ever be any human that will encounter such a problem with triple name clash :).Strangury
To make it clear for everyone else: This solution does not work as soon as you have doSomething anywhere in the inheritance hierarchy (due to how method call targets are resolved). So Knuth help you if you want to call a toString method. The solution proposed by Donal is the one that's in Bloch's Java Puzzlers book (I think, haven't looked it up), so we can consider it the authoritative answer really :-)Maltreat
L
16

The proper way of doing things would be the static import, but in the absolute worst case scenario, you COULD construct an instance of the class using reflection if you know its fully qualified name.

Java: newInstance of class that has no default constructor

And then invoke the method on the instance.

Or, just invoke the method itself with reflection: Invoking a static method using reflection

Class<?> clazz = Class.forName("net.foo.X");
Method method = clazz.getMethod("doSomething");
Object o = method.invoke(null);

Of course, these are obviously last resorts.

Longlegged answered 4/7, 2014 at 10:58 Comment(4)
This answer is by far the biggest overkill until now - thumbs up :)Strangury
You should get a completionist badge for this answer; you provided the answer for that singular poor person who has to use an ancient version of Java and solve this exact problem.Rainbolt
I actually did not think about the fact that static import feature was added only in Java 1.5. I don't envy people who need to develop for 1.4 or below, I had to once and it was terrible!Longlegged
@Gimby: Well, there is still the ((net.foo.X)null).doSomething() solution which works in old java. But if type A even contains an inner type net, THEN this is the only answer that remains valid :).Strangury
F
6

Not really THE answer but you could create an instance of X and call the static method on it. That would be a way (dirty I admit) to call your method.

(new net.foo.X()).doSomething();
Fleshings answered 4/7, 2014 at 10:43 Comment(3)
Casting null to the type and then calling the method on it would be better, since creating an object may have side effects which I do not want.Strangury
@Strangury That idea of casting null would seem to be one of the cleaner ways to do it. Needs a @SuppressWarnings("static-access") to be warning-free…Trinette
Thanks for the precision about null, but casting null has always be a heart breaker to me.Fleshings
P
4

There's no need to do any cast or suppress any strange warnings or create any redundant instance. Just a trick using the fact that you can call parent class static methods via the sub-class. (Similar to my hackish solution here.)

Just create a class like this

public final class XX extends X {
    private XX(){
    }
}

(The private constructor in this final class makes sure no one can accidentally create an instance of this class.)

Then you're free to call X.doSomething() via it:

    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }
Pop answered 5/7, 2014 at 17:24 Comment(4)
Interesting, yet another approach. However, the class with the static method is a final utility class.Strangury
Yeah, can't use this trick in that case. Yet another reason why static method is a language fail and Scala's object approach should be taken.Pop
If you're going to create another class anyway, then using a delegate class as described in blgt answer is probably the best.Lashawna
@Lashawna This avoids the extra method call and future refactoring burden though. The new class basically serves as an alias.Pop
S
2

What if you try getting the gobalnamespace given all the files are in the same folder. (http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html)

    package com.bar;
      class B extends A {

       public void doSomething(){
         com.bar.getGlobal().net.foo.X.doSomething(); // drill down from the top...

         }
     }
Sillsby answered 8/7, 2014 at 8:30 Comment(1)
How does this work? AFAIK getGlobal() is not a standard Java method for packages... (I think packages cannot have any methods in Java...)Hosfmann
D
1

This is one of the reasons that composition is preferable to inheritance.

package com.bar;
import java.util.concurrent.Callable;
public class C implements Callable<org.A>
{
    private class B extends org.A{
    public void doSomething(){
        C.this.doSomething();
    }
    }

    private void doSomething(){
    net.foo.X.doSomething();
    }

    public org.A call(){
    return new B();
    }
}
Downstream answered 4/7, 2014 at 16:36 Comment(0)
D
0

I would use the Strategy pattern.

public interface SomethingStrategy {

   void doSomething();
}

public class XSomethingStrategy implements SomethingStrategy {

    import net.foo.X;

    @Override
    void doSomething(){
        X.doSomething();
    }
}

class B extends A {

    private final SomethingStrategy strategy;

    public B(final SomethingStrategy strategy){
       this.strategy = strategy;
    }

    public void doSomething(){

        strategy.doSomething();
    }
}

Now you have also decoupled your dependency, so your unit tests will be easier to write.

Danieledaniell answered 15/7, 2014 at 11:2 Comment(2)
You've forgotten to add an implements SomethingStrategy on the XSomethingStrategy class declaration.Alpenstock
@rcdmk Thank you. Should be fixed now.Danieledaniell

© 2022 - 2024 — McMap. All rights reserved.