Monday, March 9, 2009

Friends for java

I read a blog entry of Roman Kennke. He's talking about implementing something akin to the friend class concept in java. The problem goes like this:
package api;

public class WantedCode {
void wantedMethod() {
System.out.println("Wanted method in com.example");
}
}

// --- in another package ---

package com.example

import api.WantedCode;

public class NeedingCode {
void needingMethod() {
// .. we have an instance of this class
WantedCode instance = new WantedCode();
// And need to access some package private method in that class
instance.wantedMethod() // <- error, since it is package private
}
}
The solution is to make an interface within NeedingCode's package, which declares a method that will invoke the package private method in WantedCode, taking as argument the instance of WantedCode on which the method shall be invoked. This interface will be implemented somewhere in WantedCode's package, and then statically set somewhere in NeedingCode's package. The corresponding getter is package private, and one has thereby established a "friend link" for this method which only can be used by NeedingCode's package.

I had some problems following the post since the code snippets was so fragmented, so here I've dumped the full code, and also hopefully answered my own question put forward in the post: Anyone could set the proxy instance, thereby redirecting NeedingCode's invocations to some evil code. The idea is to use a two-way "handshake" to make sure that only the friend sets the proxy, by use of a "secret" (a private Object instance).

Update: Roman Kennke pointed out the obvious: This pretty much ruins the original intent, as one then have ended up with a public facing "magic method" that is shown in the API (and one could then basically just have let the package-private method be public instead). On the other hand, this method can only be used for the sole purpose of establishing the specific friend aspect, and only to the selected other package - none other can make any use of it. Furthermore, if one can accept one such public facing static magic method in the API-package, one could use this method to bridge this package (and any package-private method in that package) to any other (internal) packages.

I've made a second attempt! That one uses introspection to verify the origin of the supplied proxy instance.

(If you use Eclipse, you can mark the entire code below (all classes in one go), Ctrl+C, then activate the project node of a project and hit Ctrl+V. You will probably have to organize imports afterwards).
package com.example;

import api.WantedCode;

public class NeedingCode {
// Driver
public static void main(String[] args) {
// .. we have an instance of this class
WantedCode code = new WantedCode();

// And access its package private method through a "friend-proxy"
FriendProxy.getProxy().invokeWantedMethodOf(code);
}
}

// ------------

package com.example;

import api.WantedCode;

public abstract class FriendProxy {

// :: The proxy interface aspect

public abstract void invokeWantedMethodOf(WantedCode instance);

// :: "Handshake" that establishes friendship

private final static Object _secret = new Object();

static {
WantedCode.makeFriends(_secret);
}

private static FriendProxy _proxy;

public static void setProxy(FriendProxy proxy, Object secret) {
if (secret != _secret) {
throw new IllegalAccessError("Cannot set proxy without correct secret.");
}
_proxy = proxy;
}

// :: Package-private accessor to get to friend's package private method.

static FriendProxy getProxy() {
return _proxy;
}
}

// ------------

package api;

import com.example.FriendProxy;

public class WantedCode {

void wantedMethod() {
System.out.println("Hi from the package-private method in the API!");
}

// :: 2nd part of "handshake" that establishes friendship

public static void makeFriends(Object secret) {
FriendProxy.setProxy(new GivingAccessToNeedingCode(), secret);
}

private static class GivingAccessToNeedingCode extends FriendProxy {
@Override
public void invokeWantedMethodOf(WantedCode instance) {
instance.wantedMethod();
}
}
}

1 comment:

  1. Bà bầu quan hệ nhiều có sao không?Quan hệ là chuyện giường chiếu của các cặp vợ chồng, nhưng khi các chị em mang bầu, thời gian này bà bầu khó khăn hơn trong việc quan hệ.
    Bà bầu nên ăn gì vào tháng cuối?Chế độ dinh dưỡng cho bà bầu tháng cuối cùng là hết sức quan trọng. Tháng này, bà bầu phải chịu nhiều áp lực khi bởi bụng bà bầu lúc này to, gây vướng nặng, đi đứng khó khăn hơn
    Bà bầu bị ho phải làm sao?Thời kì mang thai các bà bầu phải gặp nhiều khó khăn, nên không thể tránh khỏi các bệnh cảm cúm, ho, đau đầu, rối loạn tiêu hóa được.
    Cách trị táo bón cho bà bầuTáo bón là một trong những triệu chứng rối loạn tiêu hóa thường gặp ở bà bầu, triệu chứng này khiến bà bầu khó chịu, việc đi ngoài gặp nhiều khó khăn
    Các loại chó cảnh đẹp trên thế giớiChó là vật nuôi thuần chủng trong nhà, rất phổ biến và gần gũi với con người. Với những đặc tính thông minh và tình cảm, chó là vật cưng, là người bạn của trẻ em, người già, những người cô đơn
    Các giống chó đẹp nhất ở việt namNhững chú chó giờ đây không còn là vật giữ nhà thông thường mà đã được coi là những người bạn trung thành, những “vật trang trí” nhà vô cùng đáng yêu độc đáo
    Bà bầu phù chân tháng cuối mấy lần thì sinhPhù chân là tình trạng sinh lý bà bầu thường gặp phải, gây không ít bất tiện, khó khăn cho bà bầu. Hiện tượng phù chân này thường xuất hiện ở giai đoạn cuối kì mang thai
    Bà bầu sau sinh nên ăn gì?Sau khi sinh sức khỏe bà bầu thường rất yếu, vì vậy mẹ cần bổ sung nhiều dưỡng chất để giúp cơ thể lấy lại năng lượng.
    Bà bầu ra nhiều khí hư có sao không?Bà bầu ra khí hư trong thời kì mang thai là một hiện tượng thường gặp nhiều ở chị em.
    Bà bầu 28 tuần tuổi nên ăn gì?Đến tuần thứ 28 thai nhi gần như hình thành hoàn thiện các bộ phận, thời gian này thai nhi sẽ phát triển nhanh hơn
    Bà bầu sắp sinh cần chuẩn bị những gì?Đã đến tháng cuối cùng rồi, thiên thần bé nhỏ kia của bạn sắp chào đời rồi, thời gian này ắc hẳn các chị em đang bồn chồn, trông ngóng ngày thiên thần mình ra đời

    ReplyDelete