繁体   English   中英

如何在实现接口但不扩展另一个类的Java类中引用super方法?

[英]How can I reference a super method in a Java class that implements an interface but does not extend another class?

我有几个Java类,它们扩展了通用List接口的各种实现。 它们只记录添加到List中的任何内容。

LoggingArrayList如下所示。 顾名思义,它扩展了ArrayList。 LoggingLinkedList类是相同的,只是它扩展了LinkedList。

我的主要目标是避免必须复制所有公共代码,以便我可以使用不同的基类。 我试图尽可能地坚持DRY原则(不要重复自己)。

首先,请不要建议更好的登录方式。 这根本不是我的真实应用。 这只是演示我遇到的问题的简单方法。

我有两个密切相关的问题。 第一个是标题中的问题。 如何在实现接口但不扩展另一个类的Java类中引用“超级”方法?

如下所示的LoggingArrayList类工作正常但是当我将类声明从... extends ArrayList更改为... implements List时,对super.method()的三个引用不再可调用,因此我的第一个问题。

对我的第二个问题的一个很好的答案将几乎使第一个问题没有实际意义。 第二个问题是:有没有办法声明一个抽象基类,或者可能是一个扩展List的接口,使用各种add()方法的默认实现,这样我就可以简单地扩展该抽象基类或实现该接口,并且只指定什么样的List将成为具体课程的基础?

例如,我想做这样的事情:

interface LoggingList<T extends Object, L extends List<T>> extends L
{
    // overloaded methods go here as shown below 
    //  with overloaded methods declared as default for the interface
}

...然后,我可以简单地为List的每个具体实现实现一次LoggingList,而不会复制所有公共代码。 具体类可能看起来像这样,在花括号内不需要额外的代码:

public class LoggingArrayList<T extends Object> implements LoggingList<T, ArrayList<T>> {}
public class LoggingLinkedList<T extends Object> implements LoggingList<T, LinkedList<T>> {}

问题是,我提出的接口定义是无效的(不会编译),并且,除非我使LoggingList成为抽象子类而不是接口,否则下面显示的代码中对super.method的引用是不可用的然后我回到了现在的位置。

提前感谢有关如何实现我的DRY目标的任何想法。

这是我的整个LoggingArrayList类。

public abstract class LoggingArrayList<T extends Object>
    extends ArrayList<T>
{
    protected void log(T e)
    {
        System.out.println(e == null ? "null" : e.toString());
    }

    @Override
    public boolean add(T e) {
        log(e);
        // How do I reference a super.method()
        // in a class that implements an interface
        // but does not extend another class?
        return super.add(e);
    }

    @Override
    public boolean addAll(Collection<? extends T> clctn) {
        boolean anyChanges = false;
        for(T e : clctn)
        {
            // ensure that we call our overridden version of add()
            //  so it gets logged.
            anyChanges = anyChanges || add(e);
        }
        return anyChanges;
    }

    @Override
    public boolean addAll(int i, Collection<? extends T> clctn) {
        for(T e : clctn)
        {
            // ensure that we call our overridden version of add() 
            //  so it gets logged.
            add(i, e);
            i++; // keep the inserted elements in their original order
        }
        return !clctn.isEmpty();
    }

    @Override
    public T set(int i, T e) {
        log(e);
        // How do I reference a super.method()
        // in a class that implements an interface
        // but does not extend another class?
        return super.set(i, e);
    }

    @Override
    public void add(int i, T e) {
        log(e);
        // How do I reference a super.method()
        // in a class that implements an interface
        // but does not extend another class?
        super.add(i, e);
    }
}

一旦这样做将是:代表。

您可以只使用一个列表,而不是为不同类型的列表实现多个实现

public class LoggingList<T extends Object> implements List<T>

  protected List<T> superList;

  public LoggingList(List<T> anotherList) {
     superList= anotherList;
  }

  protected void log(T e) {
      System.out.println(e == null ? "null" : e.toString());
  }

  @Override
  public boolean add(T e) {
      log(e);
      return superList.add(e);
  }

而不是叫super. 你打电话给superList.

您仍然需要使构造函数new LoggingLinkedList()new LoggingList(new LinkedList()); - 但这应该不会太大......

您可以将JDK代理用于此purpuse。 只是谷歌“如何使用jdk代理类”。

这里描述了一个用例。

仅供将来参考,这是一个完整的工作解决方案,我使用代理方法,如谢尔盖莫罗佐建议。 它的核心是少于50行代码。 最后包含的一半以上代码只是实际功能的单元测试。

我不确定我是否最终会将此方法用于我的目的,但这是一个非常有用的练习。 谢谢你建议谢尔盖。

public class LoggingListProxyFactory
{
    protected static void log(String methodName, Object element)
    {
        System.out.println(methodName
                + ": ["
                + (element == null ? "null" : element.toString())
                + "]"
        );
    }

    public static <T extends Object> List<T> getProxy(final List<T> list) {
        return (List<T>) Proxy.newProxyInstance(
                list.getClass().getClassLoader(),
                new Class[]{ List.class },
                new LoggingInvocationHandler(list)
        );
    }

    private static class LoggingInvocationHandler<T>
            implements InvocationHandler
    {
        final List<T> underlyingList;

        public LoggingInvocationHandler(List<T> list) {
            this.underlyingList = list;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {
            // These are the List interface methods that we want to log.
            // In every case, the new elements happen to be the last parameter.
            //
            // boolean add(Object e)
            // void add(int index, Object element)
            // boolean addAll(Collection c)
            // boolean addAll(int index, Collection c)
            // Object set(int index, Object element)
            String methodName = method.getName();
            if( ( "add".equals(methodName)
                | "addAll".equals(methodName)
                | "set".equals(methodName)
                )
                // a few additional probably unnecessary checks
                && args != null
                && args.length == method.getParameterCount()
                && method.getParameterCount() > 0
                )
            {
                log(methodName, args[args.length-1]);
            }
            return method.invoke(underlyingList, args);
        }
    }

    public void testGetProxy() {
        List<String>[] testLists = new List[] {
            new ArrayList<>(),
            new LinkedList<>()
        };
        for(List<String> aList : testLists)
        {
            List<String> proxy = LoggingListProxyFactory.getProxy(aList);

//          aList.add(42); // type is enforced at compile time
            aList.add(aList.getClass().getSimpleName());
            aList.add("unlogged");
            aList.add(null);

//          proxy.add(42); // type is enforced at compile time
            proxy.add(proxy.getClass().getSimpleName());
            // exercise each the methods that are being logged
            proxy.add("foo");
            proxy.add(0, "bar");
            proxy.add(null);
            proxy.addAll(aList);
            proxy.addAll(7, aList);
            proxy.set(5, "five");

            System.out.println();
            System.out.println(aList.getClass().getSimpleName()
                    + ".size() = " + aList.size());
            aList.stream().forEach(System.out::println);

            System.out.println();
            System.out.println("proxy.size() = " + proxy.size());
            proxy.stream().forEach(System.out::println);
        }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM