14

I was wondering if there was any way to determine if a method represented by given java.lang.Method object overrides another methods represented by another java.lang.Method object?

I'm working on Stronlgy typed javascript, and I need to be able to be able to know if a method overrides another one in order to be able to rename both of them to a shorter name.

In this case, I am talking about the extended definition of overriding, as supported by the @Override annotation, which includes implementation of interface and abstract class methods.

I'd be happy with any solution involving either reflection directly, or using any library that already does this.

LordOfThePigs
  • 11,050
  • 7
  • 45
  • 69
  • It's certainly possible, but as far as I know there's no built in way to do it. All you have to do is check the conditions for the definition of overriding in the [JLS](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.8). – Jeffrey Aug 26 '12 at 20:55
  • I know, but since this is far from trivial, especially when we are talking about generic methods, or methods that have arguments using the generic type parameter of the declaring class. – LordOfThePigs Aug 26 '12 at 20:58
  • If you use the Override annotations on all overriding methods, you can just check for the presence of the annotation. – jeff Aug 26 '12 at 21:04
  • @jeff The Override annotation is a SOURCE annotation, meaning it is not present in the compiled file. – FThompson Aug 26 '12 at 21:07
  • The `@Override` annotation has the `retention=source`, so it won't be present in compiled classes. On top of that, the program I'm writing is supposed to analyze code written by third parties, therefore, I do not have that kind of control on the classes. – LordOfThePigs Aug 26 '12 at 21:07
  • oh, learned something new - sorry – jeff Aug 26 '12 at 21:08
  • @LordOfThePigs It is far from trivial, but I'm not sure if there's another way. – Jeffrey Aug 26 '12 at 21:22
  • A related but distinct question: given a `Method`, can you easily find whether it is an override (and if so of what)? – Jesse Glick Jan 07 '16 at 22:04

1 Answers1

5

You can simply cross-check method names and signatures.

public static boolean isOverriden(Method parent, Method toCheck) {
    if (parent.getDeclaringClass().isAssignableFrom(toCheck.getDeclaringClass())
            && parent.getName().equals(toCheck.getName())) {
         Class<?>[] params1 = parent.getParameterTypes();
         Class<?>[] params2 = toCheck.getParameterTypes();
         if (params1.length == params2.length) {
             for (int i = 0; i < params1.length; i++) {
                 if (!params1[i].equals(params2[i])) {
                     return false;
                 }
             }
             return true;
         }
    }
    return false;
}

However, since your goal is to rename methods, you might instead wish to use a bytecode analysis/manipulation library such as ASM, where you can perform the same tests as well as easily modify the methods' names if the method returns true.

FThompson
  • 28,352
  • 13
  • 60
  • 93
  • 2
    Don't forget about the `return` type, `throws` declarations, or generic compatibility. – Jeffrey Aug 26 '12 at 21:21
  • @Jeffrey Return type is checked, and `throws` declarations do not matter here, because a method is overriden if its name and signature are the same as that of a method in a parent class. Generics are also irrelevant for this same reason. – FThompson Aug 26 '12 at 21:23
  • 1
    Does return type really matter? I don't think they do, they are not part of the method signature. And then I disagree that generics are irrelevant. What if you have `class Parent` with `void foo(T param)` and `class Child extends Parent` with `void foo(String param)`. Won't your implementation fail to recognize the overriding relationship? – LordOfThePigs Aug 26 '12 at 21:26
  • Minor suggestion - Array.equals() could save some typing in that comparison loop. e.g. `return Arrays.equals(params1, params2);` – user949300 Aug 26 '12 at 21:27
  • @Vulcan `throws` declarations do matter, the overriding method may not declare a checked exception in its `throws` that is not present in the overridden method's `throws`. – Jeffrey Aug 26 '12 at 21:28
  • @LordOfThePigs Return type is indeed part of the method signature (i.e. the signature of my posted method is `Z(Ljava.lang.reflect.Method;Ljava.lang.reflect.Method;)`). That example for generics will most likely need a case added to the posted method, good point. – FThompson Aug 26 '12 at 21:28
  • @LordOfThePigs `return` types must be covariant. – Jeffrey Aug 26 '12 at 21:28
  • @Jeffrey A class won't even compile if an overriding method declares a checked exception via `throws`, so that isn't a necessary check to be made. – FThompson Aug 26 '12 at 21:30
  • Right, but if they aren't covariant the java compiler won't let the class compile anyway, so that's really not necessary, is it? Edit: beaten to it by a hair... – LordOfThePigs Aug 26 '12 at 21:30
  • @LordOfThePigs Good point; that's in the same situation as `throws` actually, so you can remove return type checking. Only name and parameter equality should be necessary for checking compiled code. – FThompson Aug 26 '12 at 21:31
  • 1
    @Vulcan, the return type is not part of the method signature according to the [JLS §8.4.2](http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.2) – LordOfThePigs Aug 26 '12 at 21:33
  • @Vulcan But as I said before, parameter equality is not that trivial when generics are involved. – LordOfThePigs Aug 26 '12 at 21:34
  • @user949300 I won't include that, because extra checks for generics are necessary. – FThompson Aug 26 '12 at 21:34
  • @LordOfThePigs My bad, you are correct. I was thinking of the bytecode signature, which consists of return type and parameter types. – FThompson Aug 26 '12 at 21:35
  • As an aside, I like questions like that, that spark a long thread of comments :-) – LordOfThePigs Aug 26 '12 at 21:36
  • @LordOfThePigs You should be able to use `getDeclaringClass().getGenericSuperclass() / getGenericInterfaces()` to get the [`ParameterizedType`](http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/ParameterizedType.html)s for all of the super types. From there you can use `getActualTypeArguments()` and `getRawType().getTypeParameters()` to construct a mapping from `TypeVariable`s to `Type`s. – Jeffrey Aug 26 '12 at 21:39
  • I'll accept this answer, hoping a better solution comes along at some point in the future... It's really a hole in the Java libraries, IMO. – LordOfThePigs Sep 11 '12 at 13:24