通过向其添加方法来扩展 Java 库
Extend a Java Library by adding methods to it
我正在尝试了解如何在 Java 中正确地 "extend" 第 3 方库(具有 类 和接口)以及如何简单地将它们用作直接替换.
我目前每次调用它时都使用 Twitter4J
library. Right now I have to manually prepend @
to the result of getScreenName()
method of User
:
twitter4j.User user = getTwitterUser();
String userHandle = "@" + user.getScreenName();
我目前已经尝试了一些方法,但没有任何效果。这就是我想出的:
package my.app;
public abstract class User implements twitter4j.User {
public String getHandle() {
return "@" + getScreenName();
}
}
这样称呼它:
User user = (User) getTwitterUser();
String userHandle = user.getHandle();
这会崩溃并显示消息:
java.lang.ClassCastException: twitter4j.UserJSONImpl cannot be cast to my.app.User
Java 对象的工作方式有点像在现实世界中。
我们打个汽车比喻。你的地方旁边有一个卖车的车库。所以你可以从车库取车:
Car car = garage.buyCar(money);
车库刚好卖的车是沃尔沃。所以,你得到一辆车,而这辆车(Car 是一个接口)实际上是一辆沃尔沃。如果你知道这辆车是沃尔沃,你可以这样做:
Volvo car = (Volvo) garage.buyCar(money);
这根本不会改变车库销售汽车的方式或车库销售的汽车类型。只是因为您知道它们是沃尔沃,所以您可以转换结果以访问沃尔沃汽车的特性。
请注意,这现在有效。但没有什么能阻止车库后来决定出售大众汽车而不是沃尔沃汽车。如果他们这样做,演员表将不再有效,这将是你的错:车库告诉你它卖汽车,但没有保证它会是沃尔沃。
现在,您希望车库改为生产保时捷。正在努力
Porsche car = (Porsche) garage.buyCar(money);
行不通。仅仅因为你非常希望这辆车成为保时捷,不会神奇地将车库生产的沃尔沃改成保时捷。
你不能那样扩展库
您所能做的就是获取 User
并调用实用程序方法来创建该用户的句柄。或者创建你自己的 UserWithHandle
class 来实现 User
,添加一个 getHandle()
方法,并通过委托给原始的、包装的 User:[=21 来实现所有其他方法=]
UserWithHandle u = new UserWithHandle(getTwitterUser())
如果库允许您提供一个 UserFactory
实现,您可以做任何您想做的事情,这将取代它以您自己的方式创建用户的方式。有点像车库允许您提供自己的油漆工。
做你想做的事,即向现有 class 添加方法在 Java 中是不可能的,但在某些语言中是可能的,例如 Kotlin(与 Java 兼容)并在 JVM 上运行,如果您有兴趣的话),它们在其中被称为 扩展方法 。基本上,这些方法是静态实用方法,例如,将 User
作为参数,return 作为其句柄,但可以像 User
的实例方法一样调用。 =21=]
您收到 ClassCastException 的原因是因为您试图将 getTwitterUser()
的结果转换为您的 User
实例,而它 returns twitter4j.User
。
如果您希望它起作用,仅创建您的 class 是不够的,您还需要创建另一种 getTwitterUser()
风格来创建它。
类似于:
public User getMyTwitterUser() {
Twitter4j tUser = getTwitterUser();
User myUser = new User(tUser);// assuming you have a constructor that builds your user from the original one
return myUser;
}
如果你想扩展一个库,你必须为它创建一个包装器并根据你的需要添加方法。但是您不能向现有库或框架添加任何方法 class。这里分享了一个例子,可以帮助你更好地理解。
Interface TwitterUserWrappable {
String getHandle();
String getDescription();
}
//use this Wrapper pattern to create methods as per your need
Class TwitterUserWrapper: TwitterUserWrappable {
private twitter4j.User user;
public TwitterUserWrapper(twitter4j.User twitterUser) {
this.user = twitterUser;
}
//Customize the return as per your need after getting screenName
@override
public String getHandle() {
return "@" + user.getScreenName();
}
//Customize the return as per your need after getting description
@Override
public String getDescrption() {
return user.getDescription() + "Customized"; // you can also customize the return as per your need
}
}
Class Demo {
public static void main(String[] args) {
TwitterUserWrappable user = TwitterUserWrapper(getTwitterUser());
user.getHandle(); //returns what you expected
}
}
我正在尝试了解如何在 Java 中正确地 "extend" 第 3 方库(具有 类 和接口)以及如何简单地将它们用作直接替换.
我目前每次调用它时都使用 Twitter4J
library. Right now I have to manually prepend @
to the result of getScreenName()
method of User
:
twitter4j.User user = getTwitterUser();
String userHandle = "@" + user.getScreenName();
我目前已经尝试了一些方法,但没有任何效果。这就是我想出的:
package my.app;
public abstract class User implements twitter4j.User {
public String getHandle() {
return "@" + getScreenName();
}
}
这样称呼它:
User user = (User) getTwitterUser();
String userHandle = user.getHandle();
这会崩溃并显示消息:
java.lang.ClassCastException: twitter4j.UserJSONImpl cannot be cast to my.app.User
Java 对象的工作方式有点像在现实世界中。
我们打个汽车比喻。你的地方旁边有一个卖车的车库。所以你可以从车库取车:
Car car = garage.buyCar(money);
车库刚好卖的车是沃尔沃。所以,你得到一辆车,而这辆车(Car 是一个接口)实际上是一辆沃尔沃。如果你知道这辆车是沃尔沃,你可以这样做:
Volvo car = (Volvo) garage.buyCar(money);
这根本不会改变车库销售汽车的方式或车库销售的汽车类型。只是因为您知道它们是沃尔沃,所以您可以转换结果以访问沃尔沃汽车的特性。
请注意,这现在有效。但没有什么能阻止车库后来决定出售大众汽车而不是沃尔沃汽车。如果他们这样做,演员表将不再有效,这将是你的错:车库告诉你它卖汽车,但没有保证它会是沃尔沃。
现在,您希望车库改为生产保时捷。正在努力
Porsche car = (Porsche) garage.buyCar(money);
行不通。仅仅因为你非常希望这辆车成为保时捷,不会神奇地将车库生产的沃尔沃改成保时捷。
你不能那样扩展库
您所能做的就是获取 User
并调用实用程序方法来创建该用户的句柄。或者创建你自己的 UserWithHandle
class 来实现 User
,添加一个 getHandle()
方法,并通过委托给原始的、包装的 User:[=21 来实现所有其他方法=]
UserWithHandle u = new UserWithHandle(getTwitterUser())
如果库允许您提供一个 UserFactory
实现,您可以做任何您想做的事情,这将取代它以您自己的方式创建用户的方式。有点像车库允许您提供自己的油漆工。
做你想做的事,即向现有 class 添加方法在 Java 中是不可能的,但在某些语言中是可能的,例如 Kotlin(与 Java 兼容)并在 JVM 上运行,如果您有兴趣的话),它们在其中被称为 扩展方法 。基本上,这些方法是静态实用方法,例如,将 User
作为参数,return 作为其句柄,但可以像 User
的实例方法一样调用。 =21=]
您收到 ClassCastException 的原因是因为您试图将 getTwitterUser()
的结果转换为您的 User
实例,而它 returns twitter4j.User
。
如果您希望它起作用,仅创建您的 class 是不够的,您还需要创建另一种 getTwitterUser()
风格来创建它。
类似于:
public User getMyTwitterUser() {
Twitter4j tUser = getTwitterUser();
User myUser = new User(tUser);// assuming you have a constructor that builds your user from the original one
return myUser;
}
如果你想扩展一个库,你必须为它创建一个包装器并根据你的需要添加方法。但是您不能向现有库或框架添加任何方法 class。这里分享了一个例子,可以帮助你更好地理解。
Interface TwitterUserWrappable {
String getHandle();
String getDescription();
}
//use this Wrapper pattern to create methods as per your need
Class TwitterUserWrapper: TwitterUserWrappable {
private twitter4j.User user;
public TwitterUserWrapper(twitter4j.User twitterUser) {
this.user = twitterUser;
}
//Customize the return as per your need after getting screenName
@override
public String getHandle() {
return "@" + user.getScreenName();
}
//Customize the return as per your need after getting description
@Override
public String getDescrption() {
return user.getDescription() + "Customized"; // you can also customize the return as per your need
}
}
Class Demo {
public static void main(String[] args) {
TwitterUserWrappable user = TwitterUserWrapper(getTwitterUser());
user.getHandle(); //returns what you expected
}
}