在java中使用“?”关键字避免列表和映射中的很长类型参数

在java中使用“?”关键字避免列表和映射中的很长类型参数,java,generics,Java,Generics,我一直在学习一个类,在这个类中,我遇到了类型值非常大的映射和列表。 例如 及 在这样的数据类型上循环会使样板代码看起来非常难看 for(Map<String, Map<String, ...>> e : map.entrySet()){ //do something... for(Map<String, ..> e1 : e.entrySet()){ ..more such loops } } 我想出了一个解决方案,使用

我一直在学习一个类,在这个类中,我遇到了类型值非常大的映射和列表。 例如

在这样的数据类型上循环会使样板代码看起来非常难看

for(Map<String, Map<String, ...>> e : map.entrySet()){
    //do something...
    for(Map<String, ..> e1 : e.entrySet()){
       ..more such loops
    }
}
我想出了一个解决方案,使用“?”关键字来减少模板的大小 下面是我的解决方案

Map<String, Map<String, Map<String, Long>>> map = ....

for(Entry<String, ?> e : map.entrySet()){
    Map<String , ?> mapLevel1 = (Map<String, ?>)e.getValue();
    for(Entry<String, ?> e1 : mapLevel1.entrySet()){
        Map<String, ?> mapLevel2Map = (Map<String, ?>) e1.getValue();
        for(Entry<String, ?> e2 : mapLevel2Map.entrySet()){
            Long data =  (Long)e2.getValue();
            .....
        }
    }
}
采用这种方法可能会有任何潜在的问题吗? 期待中的感谢

?表示某种类型,但我不知道大致是哪种类型。既然你知道那是什么类型的,那就不太合适了

采用这种方法可能会有任何潜在的问题吗

在getValue之后的任何地方都需要强制转换,而不需要强制转换?这是一个非常重要的问题,我甚至不会称之为潜在问题。如果类型的任何部分发生了更改,请好运地找到您需要更改的类型和更改的内容

编辑:自Java10以来,您只需

forvar e:map.entrySet{ var mapLevel1=e.getValue; forvar e1:mapLevel1.entrySet{ var mapLevel2Map=e1.getValue; forvar e2:mapLevel2Map.entrySet{ //还是var 长数据=e2.getValue; ..... } } } 让编译器推断类型并检查一切是否有意义。

?表示某种类型,但我不知道大致是哪种类型。既然你知道那是什么类型的,那就不太合适了

采用这种方法可能会有任何潜在的问题吗

在getValue之后的任何地方都需要强制转换,而不需要强制转换?这是一个非常重要的问题,我甚至不会称之为潜在问题。如果类型的任何部分发生了更改,请好运地找到您需要更改的类型和更改的内容

编辑:自Java10以来,您只需

forvar e:map.entrySet{ var mapLevel1=e.getValue; forvar e1:mapLevel1.entrySet{ var mapLevel2Map=e1.getValue; forvar e2:mapLevel2Map.entrySet{ //还是var 长数据=e2.getValue; ..... } } } 让编译器推断类型并检查一切是否合理

采用这种方法可能会有任何潜在的问题吗? 期待中的感谢

不可读的代码和泛型的丢失都会带来好处,因为您必须像使用原始类型一样强制转换映射方法返回的值

在任何情况下,具有如此重要的深层结构可能会由于潜在的实例化丢失而产生运行时错误,并使代码的读取和维护变得复杂

您应该改进设计,引入自定义类来包装映射,并提供逻辑方法来添加和检索数据。 你也应该把图书馆看作番石榴。 例如,是在被操纵的类型中引入某种抽象的很好的候选者

采用这种方法可能会有任何潜在的问题吗? 期待中的感谢

不可读的代码和泛型的丢失都会带来好处,因为您必须像使用原始类型一样强制转换映射方法返回的值

在任何情况下,具有如此重要的深层结构可能会由于潜在的实例化丢失而产生运行时错误,并使代码的读取和维护变得复杂

您应该改进设计,引入自定义类来包装映射,并提供逻辑方法来添加和检索数据。 你也应该把图书馆看作番石榴。
例如,在您的操作类型中引入某种抽象是一个很好的选择。

正如其他人所指出的,使用?情况更糟。不要这样做

您应该在IDE中启用所有编译器警告,或者在命令行上生成时使用-Xlint。这将告诉您,转换为泛型类型是不安全的操作

保持整洁的一个好方法是创建封装这些映射的实际数据类

例如,您可以替换以下内容:

Map<String, Map<String, Map<String, Boolean>>> map = new HashMap<>();
由以下三类支持:

public class Person {
    private final Map<String, Address> addressesByType = new HashMap<>();

    public Set<String> getAddressTypes() {
        return new HashSet<>(addressesByType.keySet());
    }

    public Address getAddress(String type) {
        return addressesByType.get(type);
    }

    public void addAddress(String type,
                           Address address) {
        Objects.requireNonNull(type);
        Objects.requireNonNull(address);
        addressesByType.put(type, address);
    }
}
以上只是一个例子。显然,在现实生活中,关键是枚举值,日期是或对象,人们可以有多个地址和多个车辆用于特定目的


您可以用类似的方式封装列表;例如,请参见其他人指出的。

,使用?情况更糟。不要这样做

您应该在IDE中启用所有编译器警告,或者在命令行上生成时使用-Xlint。这将告诉您,转换为泛型类型是不安全的操作

保持整洁的一个好方法是创建封装这些映射的实际数据类

例如,您可以替换以下内容:

Map<String, Map<String, Map<String, Boolean>>> map = new HashMap<>();
由以下三类支持:

public class Person {
    private final Map<String, Address> addressesByType = new HashMap<>();

    public Set<String> getAddressTypes() {
        return new HashSet<>(addressesByType.keySet());
    }

    public Address getAddress(String type) {
        return addressesByType.get(type);
    }

    public void addAddress(String type,
                           Address address) {
        Objects.requireNonNull(type);
        Objects.requireNonNull(address);
        addressesByType.put(type, address);
    }
}
以上只是一个例子。显然,在现实生活中,关键是枚举值,日期是或对象,人们可以有多个地址和多个车辆用于特定目的

您可以用类似的方式封装列表;例如,请参见。


使用通配符是难看和难以理解的。使用streams api使代码更清晰。创建类来表示它是什么,而不是立即使用泛型映射。如果使用Java8,则可以使用streams来保持类型系统保证并限制类型注释,这样就可以降低样板文件的级别。但是,您必须熟悉Java8流@sumit既然你刚刚接受了答案,我也被提醒了这一点,那么现在当然有更好的选择了。请参阅更新的答案。谢谢@AlexeyRomanov,我认为var在这种情况下非常有用。谢谢你更新答案!带通配符的那个很难看也很难理解。使用streams api使代码更清晰。创建类来表示它是什么,而不是立即使用泛型映射。如果使用Java8,则可以使用streams来保持类型系统保证并限制类型注释,这样就可以降低样板文件的级别。但是,您必须熟悉Java8流@sumit既然你刚刚接受了答案,我也被提醒了这一点,那么现在当然有更好的选择了。请参阅更新的答案。谢谢@AlexeyRomanov,我认为var在这种情况下非常有用。谢谢你更新答案!
public class Person {
    private final Map<String, Address> addressesByType = new HashMap<>();

    public Set<String> getAddressTypes() {
        return new HashSet<>(addressesByType.keySet());
    }

    public Address getAddress(String type) {
        return addressesByType.get(type);
    }

    public void addAddress(String type,
                           Address address) {
        Objects.requireNonNull(type);
        Objects.requireNonNull(address);
        addressesByType.put(type, address);
    }
}
public class Address {
    public static final String TYPE_HOME = "Home";
    public static final String TYPE_WORK = "Work";

    private final Map<String, Vehicle> vehiclesByType = new HashMap<>();

    public Set<String> getVehicleTypes() {
        return new HashSet<>(vehiclesByType.keySet());
    }

    public Vehicle getVehicle(String type) {
        return vehiclesByType.get(type);
    }

    public void addVehicle(String type,
                           Vehicle vehicle) {
        Objects.requireNonNull(type);
        Objects.requireNonNull(vehicle);
        vehiclesByType.put(type, vehicle);
    }
}
public class Vehicle {
    public static final String TYPE_PERSONAL = "Personal";
    public static final String TYPE_BUSINESS = "Business";

    private final Map<String, Boolean> inspectionsByDate = new HashMap<>();

    public Set<String> getInspectionDates() {
        return inspectionsByDate.keySet();
    }

    public Boolean getInspectionOutcome(String date) {
        return inspectionsByDate.get(date);
    }

    public void addInspection(String date,
                              boolean outcome) {
        Objects.requireNonNull(date);
        inspectionsByDate.put(date, outcome);
    }
}
for (String addressType : person.getAddressTypes()) {
    Address address = person.getAddress(addressType);
    for (String vehicleType : address.getVehicleTypes()) {
        Vehicle vehicle = address.getVehicle(vehicleType);
        for (String date : vehicle.getInspectionDates()) {
            boolean outcome = vehicle.getInspectionOutcome(date);
            // ...
        }
    }
}