简体   繁体   English

Java Map toString() 其中 Map 值为接口

[英]Java Map toString() where Map Value is Interface

I have many types of maps in my method.我的方法中有多种类型的地图。 I've tried to make a mapToString() method to handle them, but some of my maps have interfaces as their key value.我试图制作一个 mapToString() 方法来处理它们,但是我的一些地图将接口作为它们的键值。

    public <T> String interfaceToString(Class<T> clazz, Object instance) {
        log.info(clazz + "");

        StringBuilder toString = new StringBuilder("[ " + clazz.getSimpleName() + " - ");

        List<Method> methods = Arrays.asList(clazz.getDeclaredMethods());

        methods.forEach(method -> {
            try {
                toString.append(method.getName() + ": " + method.invoke(instance) + ", ");
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        });

        toString.replace(toString.length() - 2, toString.length() - 1, "");
        toString.append("]");

        return toString.toString();

    }

    public <T> String mapToString(Map<T, ?> map) {
        StringBuilder toString = new StringBuilder("[ ");

        for (T key : map.keySet()) {
            Object value = map.get(key);
            if (value.getClass().isInterface()) {
                toString.append(interfaceToString(value.getClass(), value));
            } else {
                toString.append("[" + key + ", " + value + "] ");
            }
        }

        toString.append(" ]");

        return toString.toString();
    }

It works for regular maps, but it just prints the object representation for map's whose key values are interfaces.它适用于常规地图,但它只打印键值为接口的地图的 object 表示。 I'm working with many interfaces, because I am using Spring JPA Projections to pull back partial parts of my database tables.我正在使用许多接口,因为我使用 Spring JPA 投影来拉回我的数据库表的部分部分。

When I was debugging, when I logged value.getClass() in the mapToString, it's class was a Proxy.当我调试时,当我在 mapToString 中记录 value.getClass() 时,它的 class 是一个代理。 Which is why the isInterface() check failed when appending to the StringBuilder.这就是附加到 StringBuilder 时 isInterface() 检查失败的原因。

Is there a better way to do this?有一个更好的方法吗?

EDIT编辑

Here is my interface.这是我的界面。 It's a Spring JPA repository projection.这是一个 Spring JPA 存储库投影。

public interface OrderGetOpenReceievablesAndHistoryView {

    Integer getNo();

    String getBillchCode();

    String getBilltlCode();

}

Here is my map:这是我的 map:

Map<Integer, OrderGetOpenReceievablesAndHistoryView> carrierOrders = new HashMap<>();

I am assigning the map's key as an Integer and it's value as OrderGetOpenReceievablesAndHistoryView.我将地图的键分配为 Integer,其值为 OrderGetOpenReceievablesAndHistoryView。

carrierOrders = orderRepository.findOpenRecievablesHistoryViewByNoIn(carrierOrderNos).stream().collect(Collectors.toMap(OrderGetOpenReceievablesAndHistoryView::getNo, Function.identity()));

I want to print the OrderGetOpenReceievablesAndHistoryView values as a normal String from the interfaceToString() method.我想将 OrderGetOpenReceievablesAndHistoryView 值打印为 interfaceToString() 方法中的普通字符串。

EDIT2编辑2

So, what I really want is a method to print out a map whose values are an interface.所以,我真正想要的是一种打印出 map 的方法,它的值是一个接口。 If I do this, it prints it out fine:如果我这样做,它会很好地打印出来:

for (Integer key : carrierOrders.keySet()) {
    OrderGetOpenReceievablesAndHistoryView value = carrierOrders.get(key);
    log.info("{} : {}, {}, {}", key, value.getBillchCode(), value.getBilltlCode(), value.getNo());
}

Since I'm working with potentially hundreds of maps whose values could be anything, I don't want to have to write this loop everytime I want to print them.因为我正在处理可能有数百个值可能是任何值的地图,所以我不想每次打印它们时都必须编写这个循环。

Here is an output from the regular for each loop:这是每个循环的常规 output:

104432581 : TAXZ443237, HJMU499371, 104432581

Here is an output from the mapToString method():这是来自 mapToString 方法()的 output:

[104406075, org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap@750bef00]

Every object is an instance of a concrete class, so value.getClass().isInterface() will never evaluate to true .每个 object 都是具体 class 的实例,因此value.getClass().isInterface()永远不会评估为true When you want a special treatment for an interface type known at compile time, ie the map's value type as declared in the generic type, you have to pass it as an argument to the method.当您想要对编译时已知的接口类型进行特殊处理时,即在泛型类型中声明的映射值类型,您必须将其作为参数传递给方法。

Eg例如

public static <V> String mapToString(Map<?,V> map, Class<? super V> valueType) {
    StringBuilder toString = new StringBuilder("[ ");
    for(Map.Entry<?,V> entry: map.entrySet()) {
        toString.append(entry.getKey()).append(": ");
        if(!valueType.isInterface()) toString.append(entry.getValue());
        else appendInterface(toString, valueType, entry.getValue());
        toString.append(", ");
    }
    if(toString.length() > 2) toString.setLength(toString.length() - 2);
    return toString.append(" ]").toString();
}

private static <V> void appendInterface(
                   StringBuilder toString, Class<V> valueType, V value) {

    toString.append("[ ").append(valueType.getSimpleName()).append(" - ");
    for(Method m: valueType.getDeclaredMethods()) {
        if(!Modifier.isStatic(m.getModifiers()) && m.getParameterCount() == 0) {
            Object o;
            try {
                o = m.invoke(value);
            } catch(ReflectiveOperationException e) {
                e.printStackTrace();
                continue;
            }
            toString.append(m.getName()).append(": ").append(o).append(", ");
        }
    }
    toString.replace(toString.length() - 2, toString.length(), "]");
}

Which you can invoke like您可以像这样调用

Map<Integer, OrderGetOpenReceievablesAndHistoryView> carrierOrders = new HashMap<>();

String s = mapToString(carrierOrders, OrderGetOpenReceievablesAndHistoryView.class);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM