Java Generic - 无法找到对象方法

时间:2015-10-12 14:52:55

标签: java generics immutability

我的界面看起来像是:

package com.sample.service;

import com.sample.model.Request;

public interface Service<T> {
    void add(T domain);
    <E> boolean update(Request<E> request);
}

Request.java的位置

package com.sample.model;

public class Request<E> {
    private final E entity;

    public Request(E entity) {
        this.entity = entity;
    }

    public E getEntity() {
        return entity;
    }
}

以下是我的实施

package com.sample.service;

import com.sample.domain.User;
import com.sample.model.Form;
import com.sample.model.Request;

public class UserServiceImpl implements Service<User> {

    @Override
    public  void add(User user) {
        System.out.println("add: " + user);
    }

    @Override
    public <Form> boolean update(Request<Form> request) {
        System.out.println("update: " + request);
        Form form = request.getEntity();
        //form.setUsername("some_username");//This line caused compile error
        return true;
    }
}

这是Form.java

package com.sample.model;

public class Form {
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public String toString() {
        return username;
    }
}

和User.java

package com.sample.domain;

public class User {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

当我编译并执行这些代码时,一切都很好。但是,当我取消注释UserServiceImpl中注释的行时,我收到了编译错误

UserServiceImpl.java:19: error: cannot find symbol
        form.setUsername("some_username");

我的问题是为什么无法找到表单对象中的setUsername方法?表单对象是可变的。

2 个答案:

答案 0 :(得分:6)

在您的代码中

public <Form> boolean update(Request<Form> request)

Form不是com.sample.model.Form。相反,它是另一个通用参数,就像

中的E一样
<E> boolean update(Request<E> request);

。编译器对E一无所知,即使您碰巧给它的名称与其中一个真实类的名称一致。编译器唯一能确定E(或同一上下文中的Form)的是它是Object实例。 Object没有任何getEntity方法。

如果您来自C ++背景,那么您正在尝试对模板进行明确的专业化。但是,Java泛型不是模板。他们不生成代码。它们只提供一些编译时检查和运行时类型转换(有一些,但不多)。查找&#34; type erasure&#34;在Java Generics的上下文中。

要实现更有用的解决方案,您可以尝试确定update()的参数必须具有哪种接口。如果存在必需的接口,请将其写为Java interface并用作参数类型。如果没有(极少数情况下),您仍然可以使用空interface进行标记,但是您需要自己重新构建参数 - 假设UserServiceImpl知道传递给update()的类型。

在以太网情况下,update()中可能不需要泛型。

答案 1 :(得分:4)

在Arkadiy的答案中解释了方法setUsername未被识别的原因。但是,您的代码还存在其他问题。

在界面中

public interface Service<T> {
    void add(T domain);
    <E> boolean update(Request<E> request);
}

E可以是任何类型。您不应该通过坚持update类型为request来尝试在具体类中实现Request<Form>,因为update方法具有不同的签名。

一种解决方案是使界面具有两个参数

public interface Service<T, E> {
    void add(T domain);
    boolean update(Request<E> request);
}

然后你需要

public class UserServiceImpl implements Service<User, Form> 

具体类中update方法的签名应为

public boolean update(Request<Form> request)

因为该方法通用。