【问题标题】:Type variable 'T' can not be resolved (with context of class com.yaml.Example)无法解析类型变量“T”(使用 com.yaml.Example 类的上下文)
【发布时间】:2019-05-15 09:51:53
【问题描述】:

我是 java 泛型的初学者,我正在尝试在我的类中使用泛型解析我的 user.yaml 文件。当我尝试解析 yaml 文件时,无法解析类型变量“T”不知道哪里出错了。

最初这只是一个普通的类,然后我实现了泛型。

my yaml file

user:
    name: Test User
    age: 30
public interface IUser {
    String getName();
    IUser  setName(String name);

    Integer getAge();
    IUser setAge(Integer age);
}

public class User implements IUser{

    @JsonProperty("name")
    private String name;
    @JsonProperty("age")
    private Integer age;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    @JsonProperty("name")
    public String getName() {
        return name;
    }

    @JsonProperty("name")
    public User setName(String name) {
        this.name = name;
        return this;
    }

    @JsonProperty("age")
    public Integer getAge() {
        return age;
    }

    @JsonProperty("age")
    public User setAge(Integer age) {
        this.age = age;
        return this;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("name", name).append("age", age).append("additionalProperties", additionalProperties).toString();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(additionalProperties).append(age).append(name).toHashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        }
        if ((other instanceof User) == false) {
            return false;
        }
        User rhs = ((User) other);
        return new EqualsBuilder().append(additionalProperties, rhs.additionalProperties).append(age, rhs.age).append(name, rhs.name).isEquals();
    }
}
public interface IExample {
    <T extends IUser> T getUser();
    <T extends IUser> void setUser(T user);
}

public class Example implements IExample{

    @JsonProperty("user")
    private User user;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    @JsonProperty("user")
    public <T extends IUser> T getUser() {
        return (T) user;
    }

    @JsonProperty("user")
    public <T extends IUser> void setUser(T t) {
        this.user = (User)t;
    }

    @JsonAnyGetter
    public Map<String, Object> getAdditionalProperties() {
        return this.additionalProperties;
    }

    @JsonAnySetter
    public void setAdditionalProperty(String name, Object value) {
        this.additionalProperties.put(name, value);
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("user", user).append("additionalProperties", additionalProperties).toString();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(additionalProperties).append(user).toHashCode();
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        }
        if ((other instanceof Example) == false) {
            return false;
        }
        Example rhs = ((Example) other);
        return new EqualsBuilder().append(additionalProperties, rhs.additionalProperties).append(user, rhs.user).isEquals();
    }
}
my main class where i'm trying to parse yaml object

public class ReadYaml {

    public void parse(){
        String path = "path/to/user.yaml";
        ObjectMapper mapper = new ObjectMapper(new YAMLFactory());

        try {
             IExample example = mapper.readValue(new File(path), Example.class);
             System.out.println(example.getUser().getName());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args){
      new ReadYaml().parse();
    }
}

我希望输出是:Test User,但实际输出是 com.fasterxml.jackson.databind.JsonMappingException: Type variable 'T' can not be resolved (with context com.yaml.Example 类)


更新

updated yaml file

user:
    name: Test User
    age: 30
address:
    line1: bangalore
    line2: karnataka
public interface IAddress {

    String getLine1();
    IAddress setLine1(String line1);

    String getLine2();
    IAddress setLine2(String line2);
}
public class Address implements IAddress {
    // code getters and setters
}
public interface IExample<T, U extends IUser & IAddress> {
    T getUser();
    void setUser(T user);

    U getAddress();
    void setAddress(U address);
}
public class Example<T, U extends IUser & IAddress> implements IExample<T, U>{
now i'm trying to parse yaml file.
public class ReadYaml {
.
.
.

TypeReference<Example<User, Address> typeReference = new TypeReference<Example<User, Address>() {
            };
IExample example = mapper.readValue(new File(path), typeReference);
System.out.println(example.getAddress.getLine1());

我在声明 TypeReference&lt;Example&lt;User, Address&gt; typeReference 时遇到错误:类型参数“地址”不在其范围内,应实施“IUser”

【问题讨论】:

    标签: java generics compilation jackson yaml


    【解决方案1】:

    由于java's type erasure,您的代码有两个问题;这要求杰克逊知道所有在反序列化数据时要使用的具体类型。对于泛型类型,它需要知道要使用的泛型类的边界(参数化类型的具体类)。 Jackson 通过类型引用获知这个界限;您需要提供的。那么你遇到的两个问题是:

    1. 据我所知,Jackson 无法被告知泛型方法的范围(而且它没有多大意义)。因此,在Example 中,属性user (public &lt;T extends IUser&gt; void setUser(T t)) 代表了一个障碍,因为杰克逊无法知道使用哪个T 来反序列化/写入。

    2. 在调用mapper.readValue 时,需要将要读取的类型(包括有界的类型)明确告知杰克逊;这是丢失的(你只是在使用Example.class):

      IExample example = mapper.readValue(new File(path), Example.class);
      

    所以,要解决这些问题,你需要改变:

    1. 用泛型类替换IExample/Example中的泛型方法:
    ...
        static interface IExample<T extends IUser> {
            T getUser();
            void setUser(T user);
        }
    
        static class Example<T extends IUser> implements IExample<T> {
    
            @JsonProperty("user")
            private T user;
            @JsonIgnore
            private Map<String, Object> additionalProperties = new HashMap<String, Object>();
    
            @JsonProperty("user")
            public T getUser() {
                return user;
            }
    
            @JsonProperty("user")
            public void setUser(T t) {
                this.user = t;
            }
    ...
    
    1. 通过传递适当的杰克逊TypeReference 来通知杰克逊Example 有界类型:
    ...
        TypeReference<Example<User>> typeReference = new TypeReference<Example<User>>() { };
        IExample example = mapper.readValue(new File(path), typeReference);
    ...
    

    希望这会有所帮助。

    附录:

    你发现的新问题,关于额外的泛型类型IAddress/Address;不在TypeReference 附近,您正确设置为:

        TypeReference<Example<User, Address>> typeReference = new TypeReference<Example<User, Address>>() { };
    

    问题在于接口和类中泛型类型的声明。

    当您说U extends IUser &amp; IAddress 时,您是在说U 扩展/实现BOTH IUserIAddress;这就是杰克逊抱怨Address没有扩展IUser的原因;因为它只扩展IAddress。此外,T 将被隐式限制为能够绑定到任何扩展 java.lang.Object 的类(而不是 IUser;这可能是您所期望的),因为您没有为它指定 extends...

    所以,为了解决这个问题,替换 IExampleExample 的泛型类型声明:

        <T, U extends IUser & IAddress>
    

        <T extends IUser, U extends IAddress>
    

    Complete code on GitHub

    【讨论】:

    • 这很好用,谢谢 Marco。所以我在我的 yaml 文件中添加了额外的对象。用户:姓名:测试用户年龄:30 地址:line1:bangalore line2:karnataka
    • 当我尝试使用以下代码阅读相同内容时 public interface IExample&lt;T, U extends IUser &amp; IAddress&gt; { public class Example&lt;T, U extends IUser &amp; IAddress&gt; implements IExample&lt;T, U&gt;{ TypeReference&lt;Example&lt;User, Address&gt;&gt; typeReference = new TypeReference&lt;Example&lt;User, Address&gt;&gt;() { }; I'm getting *Address* is not within its bound, should implement *IUser*
    • 不客气。您能否将这些“有问题的”改进添加到问题详细信息中?
    • Marco,再次感谢它的工作:),学到了一些对我的项目很有帮助的东西:)
    猜你喜欢
    • 2016-06-07
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 1970-01-01
    • 1970-01-01
    • 2014-12-01
    • 2016-10-12
    • 1970-01-01
    相关资源
    最近更新 更多