通过向 Java 库添加方法来扩展它

She*_*yar 6 java casting twitter4j

我试图了解如何在 Java 中正确“扩展”第 3 方库(包括类和接口)以及如何简单地将它们用作替代品。

我目前正在使用Twitter4J图书馆。现在我必须手动添加每次调用方法@的结果:getScreenName()User

twitter4j.User user = getTwitterUser();
String userHandle   = "@" + user.getScreenName();
Run Code Online (Sandbox Code Playgroud)

我目前已经尝试了一些东西,但没有任何效果。这是我想出的:

package my.app;

public abstract class User implements twitter4j.User {
    public String getHandle() {
        return "@" + getScreenName();
    }
}
Run Code Online (Sandbox Code Playgroud)

像这样称呼它:

User user = (User) getTwitterUser();
String userHandle = user.getHandle();
Run Code Online (Sandbox Code Playgroud)

这与消息崩溃:

java.lang.ClassCastException: twitter4j.UserJSONImpl cannot be cast to my.app.User
Run Code Online (Sandbox Code Playgroud)

JB *_*zet 6

Java 对象的工作方式有点像在现实世界中。

让我们做一个汽车类比。你的房子旁边有一个车库,卖汽车。所以你可以从车库取车:

Car car = garage.buyCar(money);
Run Code Online (Sandbox Code Playgroud)

车库出售的汽车是沃尔沃。所以,你有一辆汽车,这辆车(汽车是一个接口)实际上是一辆沃尔沃。如果你知道这辆车是沃尔沃,你可以这样做:

Volvo car = (Volvo) garage.buyCar(money);
Run Code Online (Sandbox Code Playgroud)

这根本不会改变车库销售汽车的方式或车库销售的汽车类型。只是,因为您知道它们是沃尔沃,您可以将结果转换为访问沃尔沃汽车的特性。

请注意,这现在有效。但没有什么能阻止车库后来决定出售大众汽车而不是沃尔沃汽车。如果他们这样做,演员将不再工作,这将是你的错:车库告诉你它卖汽车,但没有承诺它会是沃尔沃。

现在,您希望车库改为生产保时捷。试图做

Porsche car = (Porsche) garage.buyCar(money);
Run Code Online (Sandbox Code Playgroud)

不会工作。仅仅因为您非常希望汽车成为保时捷,并不会神奇地将车库生产的沃尔沃变成保时捷。


您不能以这种方式扩展库

您所能做的就是获取User并调用从该用户创建句柄的实用程序方法。或者创建您自己的UserWithHandle类来实现User,添加一个getHandle()方法,并通过委托给原始的包装用户来实现所有其他方法:

UserWithHandle u = new UserWithHandle(getTwitterUser())
Run Code Online (Sandbox Code Playgroud)

如果库允许你提供一个UserFactory实现,你可以做你想做的,这将取代它以你自己的方式创建用户的方式。有点像车库允许您提供自己的画家。


做你想做的事,即将一个方法添加到现有的类在 Java 中是不可能的,但在一些语言中是可能的,比如 Kotlin(它与 Java 兼容并在 JVM 上运行,如果你感兴趣的话),它们在哪里称为扩展方法。基本上,这些方法是静态实用程序方法,例如,将 aUser作为参数并返回其句柄,但可以像调用User.