10
<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // Here comes Late Static Bindings
    }
}

class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}

B::test(); // Outputs "B"
?>

I want to get an equivalent in Java...so something like

class A {
    public static void who(){
        System.out.println("A");
    };

    public static void test(){
        who(); //<<< How to implement a static:: thing here???
    }
}

class B extends A {
     public static void who(){
        System.out.println("B");
     };

     public static void main(String[] args){
        B.test(); // Outputs "A" but I want "B"
     }
}

I want the who() call inside A::test to resolve as in PHP 5.3 by calling B::who.

EDIT: I know there is no "standard way" of doing this in most popular languages. I'm looking for hacks and such. Also, is this possible in C/C++, or any other popular OOP language?

This is not for any real design on anything. I'm just being curious.

jetru
  • 1,964
  • 4
  • 16
  • 24
  • That's a bummer. So, because it's a static function, it's not technically overridable and the ability to call it as B.test() is only just a small convenience given to users. Thus, it can take the freedom to throw away the type information at the call site. Am I correct? – jetru Jul 16 '12 at 22:00
  • Ok, I'll leave this on for a while, someone brilliant might have a cool idea. – jetru Jul 17 '12 at 21:58
  • I was incorrect about the compiler. See my answer, though it doesn't solve the full problem yet. – David Harkness Jul 17 '12 at 22:48
  • This is call as shadowing – subhashis Mar 20 '19 at 11:54

5 Answers5

2

Not possible in Java. (At least not without ugly reflection hacks.)

I encourage you to rethink your design and rely on proper objects.

Related question:

Edit: B.test() will (or can at least according to spec) be compiled into a call to A.test(), so there's no way to discover how the call was made from within A.test(). In other words, there's no way to let the behaviour of A.test depend on if it was called through A.test() or B.test().

Since you're asking out of curiosity, here's AFAIK the closest "solution".

  1. Overload test with a test(Class<?> c) which takes as argument the class which defines the intended who method.
  2. Hide (note that you can't override) test() in class B.
  3. And change the implementation of A.test slightly.

In code:

class A {
    public static void who() {
        System.out.println("A");
    }

    public static void test() {
        test(A.class);
    }

    public static void test(Class<?> c) {
        //who(); //<<< How to implement a static:: thing here???
        try {
            c.getMethod("who").invoke(null); // Call static who on given class.
        } catch (Exception e) {
        }
    }

}

public class B extends A {
     public static void who(){
        System.out.println("B");
     }

     public static void test() {
         test(B.class);
     }

     public static void main(String[] args){
        A.test(); // Outputs "A"
        B.test(); // Outputs "B"
     }
}
Community
  • 1
  • 1
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • Yes, I'm looking for ugly reflection hacks. This is not a real design for anything. I learnt this about PHP and I'm wondering as to how comparable constructs in other languages will look like. – jetru Jul 16 '12 at 21:48
2

It seems that the compiler generates a call to B.test in the bytecode even though B doesn't declare a method named test.

Bytecode of main method:

invokestatic #5 = Method B.test(()V)
return

Given the names of a class and method ("B" and "who") you can easily use reflection to call the method. So the question becomes

Can you extract B by combining the call stack and bytecode inside A.test?

You'll need to use the return address stored on the stack to locate the call to B.test in the bytecode and extract the declared call. There are plenty of bytecode manipulation libraries, but I don't know if any of them allow you to tie that to the execution stack in the JVM.

David Harkness
  • 35,992
  • 10
  • 112
  • 134
1

You can't override static methods in java.

http://geekexplains.blogspot.co.uk/2008/06/can-you-override-static-methods-in-java.html

Alan Hay
  • 22,665
  • 4
  • 56
  • 110
1

Here's an example from Java. It uses Java 8 default methods and getClass(). I bet it works with classes too:

interface A {
    default String name() {
       return getClass().getName();
    }
}

class B implements A {}


public class LateBinding {
    public static void main(String[] args) {

        // Create an anonymous class in `LateBinding` (called `$1`)
        System.out.println(new A(){}.name());

        // Instantiate a new `B`
        B b = new B();
        System.out.println(b.name());
    }
}

Results:

$ javac LateBinding.java && java LateBinding
LateBinding$1
B

As you can see the method knows in both cases where it's running, although it's defined in A. This example is not really static, because you can't call getClass() statically, but LSB in PHP is not really limited to static contexts.

Dmitry Minkovsky
  • 36,185
  • 26
  • 116
  • 160
0

There is no elegant way to do it with static method declaration (Only Delphi from what I'm aware of supports override for static methods). However if static is not necessary for you you can write something like this:

class A {
    public void who(){
        System.out.println("A");
    };

    public void test(){
        who(); //<<< How to implement a static:: thing here???
    }
}

class B extends A {
    @Override
    public void who(){
        System.out.println("B");
    };
    public void main(String[] args){
        A instance = new A();
        instance.test(); // prints 'A'

        instance = new B();
        instance.test(); // prints 'B'
    }
} 

EDIT after clarification: Pretty hacky way of doing this: Thread.currentThread().getStackTrace() then from top-most record get method and class this method belongs to. Having Class c - you could write c.getMethod("who").invoke(null); to call the correspond who() method.

Community
  • 1
  • 1