如何禁止用户复制getter返回的引用?

时间:2017-07-01 01:08:24

标签: java

public class Bar {
    // Third party class -- I don't want to rely on its current interface.
}

public class Foo {
    private /* not final */ Bar bar;

    public refreshBar() {
        bar = new Bar();  // Old references are now "dead".
    }

    /** Question: The reference this method returns can change. */
    public Bar getBar() {
        return bar;
    }
}


Foo foo = new Foo();

foo.getBar().doThing();  // OK.
Bar bar = foo.getBar();  // BAD - I want this to be forbidden.
bar.doThing();           // ERROR.

如何在编译时进行BAD换行?如果不能这样做,那我怎么能让它抛出异常呢?它甚至可以受到影响吗?

4 个答案:

答案 0 :(得分:3)

简单。 不要公开Bar并公开您想要的任何方法:

public class Foo {
    private Bar bar;

    public refreshBar() {
        bar = new Bar();
    }

    public void doOneThing() {
        bar.firstThing();
    }

    public void doAnotherThing() {
        bar.secondThing();
    }
}

答案 1 :(得分:1)

简短而令人沮丧的答案是:完全没有。这根本不可能。有实现所需行为的方法,但只有在为此应用程序模式设置设计的类时。事后看来,对于#34;遗留代码",可能并不那么容易。

评论中已经提到了一个明显的解决方案:不要通过Bar公开getBar。你可以代替提供所有公共方法

public class Foo {
    private /* not final */ Bar bar;

    //public Bar getBar() { return bar; } // NOPE

    public void doThis() { bar.doThis(); }
    public void doThat() { bar.doThat(); }
}    

但是我会不推荐这个,原因很明显:你评论了Bar类有一个

// Big mess of public methods.

你确实想要用所有这些方法污染你的Foo课程。

编辑:后来更改了评论,而是说Bar是第三方类,其中有一个提问者不想依赖的界面。这里概述的解决方案可以应用,但在这种情况下,也可以作为缓冲 - 在某种程度上 - 甚至可以帮助防止不兼容的变化

我可以想象另一种方法,可能有点清洁:

  1. Bar中提取接口,其中包含您需要的所有公共方法。这通常可以通过IDE(例如Eclipse:" Extract interface")来实现。我们称之为BarInterface

  2. BarInterface班级返回Foo的实例:

    public BarInterface getBar() { ... }
    
  3. 不要返回bar,而是返回bar的包装。基本上,BarInterface的实现只是委托给Bar实例。这基本上是一个适配器模式。

  4. interface BarInterface {
        public void doThis();
        public void doThat();
    }
    
    public class Bar 
        public void doThis() { ... }
        public void doThat() { ... }
    }
    
    class BarWrapper implements BarInterface {
        private Bar delegate; 
        void setDelegate(Bar delegate) { this.delegate = delegate; }
        @Override public void doThis() { delegate.doThis(); }
        @Override public void doThat() { delegate.doThat(); }
    }
    
    
    public class Foo {
        private /* not final */ Bar bar;
    
        // This can be final!
        private final BarWrapper barWrapper = new BarWrapper();
    
        public refreshBar() {
            bar = new Bar();  // Old references are now "dead".
            barWrapper.setDelegate(bar);
        }
    
        // The reference returned here will never change!
        public BarInterface getBar() {
            return barWrapper;
        }
    }
    

    当然,这需要付出一些努力(特别是当Bar类中有很多方法时)。但它可能允许您解决原始问题:人们仍然可以获取他们心爱的Bar(Interface) - 引用并存储它。对refreshBar()的任何调用都将导致这些引用透明地更新。

    但当然,您必须确定它在您的确切应用案例中是否真的适用或有益。

答案 2 :(得分:0)

以下是为您的目的演示一个简单的包装器:

public class BarWrapper {
    public static void doThing() {
        foo.getBar.doThing();
    }
}

public static void Main( String[] Args) {
    foo.getBar // ERROR: does not import FOO
    BarWrapper.doThing(); // WORKS

}

答案 3 :(得分:0)

如果Bar是一个接口,您可以为该接口创建一个Proxy对象并将其返回。然后,当调用该代理接口上的任何方法时,您可以获取当前的Bar对象,并将该调用传递给该实例。

请参阅:Proxy文档。