Class VirtualMethod<C>


  • public final class VirtualMethod<C>
    extends java.lang.Object
    A utility for keeping backwards compatibility on previously abstract methods (or similar replacements).

    Before the replacement method can be made abstract, the old method must kept deprecated. If somebody still overrides the deprecated method in a non-final class, you must keep track, of this and maybe delegate to the old method in the subclass. The cost of reflection is minimized by the following usage of this class:

    Define static final fields in the base class (BaseClass), where the old and new method are declared:

      static final VirtualMethod<BaseClass> newMethod =
       new VirtualMethod<BaseClass>(BaseClass.class, "newName", parameters...);
      static final VirtualMethod<BaseClass> oldMethod =
       new VirtualMethod<BaseClass>(BaseClass.class, "oldName", parameters...);
     

    This enforces the singleton status of these objects, as the maintenance of the cache would be too costly else. If you try to create a second instance of for the same method/baseClass combination, an exception is thrown.

    To detect if e.g. the old method was overridden by a more far subclass on the inheritance path to the current instance's class, use a non-static field:

      final boolean isDeprecatedMethodOverridden =
       AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
        (oldMethod.getImplementationDistance(this.getClass()) > newMethod.getImplementationDistance(this.getClass())));
    
      // alternatively (more readable):
      final boolean isDeprecatedMethodOverridden =
       AccessController.doPrivileged((PrivilegedAction<Boolean>) () ->
        VirtualMethod.compareImplementationDistance(this.getClass(), oldMethod, newMethod) > 0);
     

    It is important to use AccessController.doPrivileged(PrivilegedAction) for the actual call to get the implementation distance because the subclass may be in a different package. The static constructors do not need to use AccessController because it just initializes our own method reference. The caller should have access to all declared members in its own class.

    getImplementationDistance(java.lang.Class<? extends C>) returns the distance of the subclass that overrides this method. The one with the larger distance should be used preferable. This way also more complicated method rename scenarios can be handled (think of 2.9 TokenStream deprecations).

    • Field Summary

      Fields 
      Modifier and Type Field Description
      private java.lang.Class<C> baseClass  
      private java.lang.ClassValue<java.lang.Integer> distanceOfClass  
      private java.lang.String method  
      private java.lang.Class<?>[] parameters  
      private static java.util.Set<java.lang.reflect.Method> singletonSet  
    • Constructor Summary

      Constructors 
      Constructor Description
      VirtualMethod​(java.lang.Class<C> baseClass, java.lang.String method, java.lang.Class<?>... parameters)
      Creates a new instance for the given baseClass and method declaration.
    • Method Summary

      All Methods Static Methods Instance Methods Concrete Methods 
      Modifier and Type Method Description
      static <C> int compareImplementationDistance​(java.lang.Class<? extends C> clazz, VirtualMethod<C> m1, VirtualMethod<C> m2)
      Utility method that compares the implementation/override distance of two methods.
      int getImplementationDistance​(java.lang.Class<? extends C> subclazz)
      Returns the distance from the baseClass in which this method is overridden/implemented in the inheritance path between baseClass and the given subclass subclazz.
      boolean isOverriddenAsOf​(java.lang.Class<? extends C> subclazz)
      Returns, if this method is overridden/implemented in the inheritance path between baseClass and the given subclass subclazz.
      (package private) int reflectImplementationDistance​(java.lang.Class<?> subclazz)  
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Field Detail

      • singletonSet

        private static final java.util.Set<java.lang.reflect.Method> singletonSet
      • baseClass

        private final java.lang.Class<C> baseClass
      • method

        private final java.lang.String method
      • parameters

        private final java.lang.Class<?>[] parameters
      • distanceOfClass

        private final java.lang.ClassValue<java.lang.Integer> distanceOfClass
    • Constructor Detail

      • VirtualMethod

        public VirtualMethod​(java.lang.Class<C> baseClass,
                             java.lang.String method,
                             java.lang.Class<?>... parameters)
        Creates a new instance for the given baseClass and method declaration.
        Throws:
        java.lang.UnsupportedOperationException - if you create a second instance of the same baseClass and method declaration combination. This enforces the singleton status.
        java.lang.IllegalArgumentException - if baseClass does not declare the given method.
    • Method Detail

      • getImplementationDistance

        public int getImplementationDistance​(java.lang.Class<? extends C> subclazz)
        Returns the distance from the baseClass in which this method is overridden/implemented in the inheritance path between baseClass and the given subclass subclazz.
        Returns:
        0 iff not overridden, else the distance to the base class
      • isOverriddenAsOf

        public boolean isOverriddenAsOf​(java.lang.Class<? extends C> subclazz)
        Returns, if this method is overridden/implemented in the inheritance path between baseClass and the given subclass subclazz.

        You can use this method to detect if a method that should normally be final was overridden by the given instance's class.

        Returns:
        false iff not overridden
      • reflectImplementationDistance

        int reflectImplementationDistance​(java.lang.Class<?> subclazz)
      • compareImplementationDistance

        public static <C> int compareImplementationDistance​(java.lang.Class<? extends C> clazz,
                                                            VirtualMethod<C> m1,
                                                            VirtualMethod<C> m2)
        Utility method that compares the implementation/override distance of two methods.
        Returns:
        • > 1, iff m1 is overridden/implemented in a subclass of the class overriding/declaring m2
        • < 1, iff m2 is overridden in a subclass of the class overriding/declaring m1
        • 0, iff both methods are overridden in the same class (or are not overridden at all)