无法解析类型变量“ T”(使用com.yaml.Example类的上下文)

时间:2019-05-15 09:51:53

标签: java jackson jackson-databind

我是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:无法解析类型变量'T'(具有上下文) 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<Example<User, Address> typeReference的声明中遇到错误:类型参数“地址”不在其范围内,应实现“ IUser”

1 个答案:

答案 0 :(得分:2)

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

  1. 据我所知,无法告知Jackson泛型方法的范围(这没有多大意义)。因此,在Example中,属性userpublic <T extends IUser> void setUser(T t))表示一个障碍,因为杰克逊无法知道使用哪个T来反序列化/写入。 p>

  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. 通过传递适当的杰克逊Example来通知TypeReference有界类型的杰克逊:
...
    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 & IAddress时,是说U扩展/实现了两者 IUserIAddress;这就是杰克逊抱怨Address没有扩展IUser的原因;因为它仅扩展IAddress。此外,由于T没有被指定为可以绑定到任何扩展java.lang.Object的类(而不是IUser;这是您可能期望的),因此可以绑定到该类。为此extends...

因此,要解决此问题,请替换IExampleExample的通用类型声明:

    <T, U extends IUser & IAddress>

使用

    <T extends IUser, U extends IAddress>