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

时间:2016-04-30 07:23:38

标签: java casting twitter4j

我正在尝试理解如何在Java中正确地“扩展”第三方库(包括类和接口)以及如何简单地将它们用作插入式替换。

我目前正在使用Twitter4J库。现在我每次打电话时都必须手动将@ getScreenName()方法的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

这与消息崩溃:

public function unsetNonDefaultBillingAddresses($userId, $userAddressId)
{
    $builder1 = $this->getEntityManager()->createQueryBuilder();
    $subQuery = $builder1->select('a1.userAddressId')
            ->from('Application\Entity\UserAddresses', 'a1')
            ->leftJoin('a1.user', 'u')
            ->where('u.userId = ?1');

    $builder2 = $this->getEntityManager()->createQueryBuilder();
    $builder2->update('Application\Entity\UserAddresses', 'a2')
            ->set('a2.defaultBillingAddress', 0)
            ->where($builder2->expr()->in('a2.userAddressId', $subQuery->getDQL()))
            ->andWhere('a2.userAddressId != ?2')
            ->setParameter(1, $userId)
            ->setParameter(2, $userAddressId);

    \Freedom\Logger\InfoLogger::vardump($builder2->getDQL());
    return $builder2->getQuery()->execute();
}

3 个答案:

答案 0 :(得分:3)

Java对象的工作方式与现实世界类似。

让我们做一个汽车比喻。你的汽车旁边有一个车库。所以你可以从车库买车:

Car car = garage.buyCar(money);

车库碰巧卖的车是沃尔沃。所以,你得到一辆车,而这辆车(Car是一个接口)实际上是一辆沃尔沃。如果您知道汽车是沃尔沃汽车,那么您可以这样做:

Volvo car = (Volvo) garage.buyCar(money);

这根本不会改变车库出售汽车的方式,也不会改变车库销售的汽车类型。就是这样,因为你知道它们是volvos,你可以投射结果来获得沃尔沃汽车的特性。

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

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

Porsche car = (Porsche) garage.buyCar(money);

不起作用。只是因为你非常希望汽车成为保时捷,不会神奇地改变车库生产的沃尔沃保时捷。

您不能以这种方式扩展库

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

UserWithHandle u = new UserWithHandle(getTwitterUser())

如果库允许您提供UserFactory实现,那么您可以按照自己的方式替换其创建用户的方式。有点像车库允许你提供自己的画家。

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

答案 1 :(得分:1)

您获得ClassCastException的原因是因为您尝试将getTwitterUser()的结果强制转换为User实例,而它返回twitter4j.User

如果你希望它能够起作用,那么创建你的课程是不够的,你还需要创建另一种可以创建它的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;
}

答案 2 :(得分:0)

如果你想扩展一个库,你必须为它创建一个包装器并根据需要添加方法。但是您不能向现有库或框架类添加任何方法。这里分享了一个例子,可以帮助你更好地理解。

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
 }
}