将具有lambda的类映射到默认值

时间:2017-07-27 13:12:06

标签: java lambda java-8

来自this的问题。 有这样的层次结构。 A是基类的地方:

       A 
      / \
     B   C

  |   A    |   |   B       |   |  C      |  
  | getId()|   |A.getId()  |   |A.getId()|
               |isVisible()| 

以及以下内容:

List<A> mappings;

我想将 B 实例的所有ID映射到B.isVisible()的值,将 C 的实例ID映射到 TRUE < /强>

在初始问题的帮助下,我将其改进为这种格式:

mappings.stream().filter(a -> a instanceof B)
                 .map(b -> (B)b)
                 .collect(Collectors.toMap(A::getId, m -> m.isVisible()));

丑陋的版本是:

mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m ->
                        {
                            boolean isB = m instanceof B;
                            return isB ? ((B) m).isVisible() : true;
                        }));

有关改进它的任何帮助,以便为更优雅的版本提供默认值?

6 个答案:

答案 0 :(得分:7)

你的变种

mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m ->
                        {
                            boolean isB = m instanceof B;
                            return isB ? ((B) m).isVisible() : true;
                        }));

并不是那么难看,因为它表达了你的意图。

但是你可以简化它,因为你不需要一个局部变量来保存m instanceof B

mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m->m instanceof B? ((B)m).isVisible(): true));

然后,根据经验,只要复合布尔表达式中有boolean个文字,就会有一个没有它的替代品。

mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m -> !(m instanceof B) || ((B)m).isVisible()));

答案 1 :(得分:3)

也许你可以通过助手类做你想做的事情:

class Helper {
    private final Long id;
    private final boolean visible;

    Helper(A a) {
        this.id = a.getID();
        this.visible = a instanceof B ? ((B) a).isVisible() : true;
    }

    Long getId() { return id; }

    boolean isVisible() { return visible; }
}

然后,将列表的每个元素映射到Helper的实例并收集到地图:

Map<Long, Boolean> map = mappings.stream()
    .map(Helper::new)
    .collect(Collectors.toMap(Helper::getId, Helper::isVisible));

此解决方案只是将visible true falseHelper委托给Boolean类,并让您拥有一个干净的流管道。

作为附注...通常,使用类型为Set的值的地图是没有意义的,因为您可以使用Set<Long> set = mappings.stream() .map(Helper::new) .filter(Helper::isVisible) .collect(Collectors.toSet()); 具有相同的语义:

boolean isVisible = set.contains(elementId);

然后,要知道某个元素是否可见,只需检查它是否属于集合:

$destinations = get_posts( array(
                            'post_type' => 'destination_showcase',
                            'posts_per_page' => -1,
                            'post_status' => 'publish',
                            'meta_query' => array(
                                array(
                                    'key' => 'destination_state',
                                    'value' => ':"'.$state_id . '";' , // looking for serialized value in quotes
                                    'compare' => 'LIKE'
                                )
                            ),
                            'orderby' => 'title',
                            'order'   => 'ASC',
                        ) );

答案 2 :(得分:3)

如果您无法更改源代码,可以编写实用程序方法isA来描述您想要的内容,例如:

Map<Integer, Boolean> visibility = mappings.stream().collect(toMap(
        A::getId,
        isA(B.class, B::isVisible, any -> true)
));
static <T, S extends T, R> Function<T, R> isA(Class<? extends S> type,
                                              Function<? super S, R> special,
                                              Function<T, R> general) {

    return it -> type.isInstance(it) ? special.apply(type.cast(it))
                                     : general.apply(it);
}

答案 3 :(得分:2)

您的代码很难看,因为您的层次结构没有意义。你可能想要的是:

class A
{
    abstract public boolean isVisible();
    // or make it concrete and return a default if you need to
}

// B can stay the same (+ @Override)

class C extends A
{
    @Override
    public boolean isVisible()
    {
        return true;
    }
}

然后就可以了:

mappings.stream()
        .collect(Collectors.toMap(A::getId, m -> m.isVisible()));

答案 4 :(得分:1)

可能在流中将C&C映射为null,然后在null上返回true?像这样:

mappings.stream().map(a -> a instanceof C ? (B)null : (B)a)
                 .collect(Collectors.toMap(A::getId, m==null || m.isVisible()));

答案 5 :(得分:0)

我已经编写了这个简单的Collector实现,它可以做你想做的事情:

public class AToMapCollector implements Collector<A, Map<Integer, Boolean>, Map<Integer, Boolean>>{
    @Override
    public Supplier<Map<Integer, Boolean>> supplier(){
        return HashMap::new;
    }

    @Override
    public BiConsumer<Map<Integer, Boolean>, A> accumulator(){
        return (map, a) -> {
            boolean visible = true;
            if(a instanceof B){
                visible = ((B) a).isVisible();
            }
            map.put(a.getId(), visible);
        };
    }

    @Override
    public BinaryOperator<Map<Integer, Boolean>> combiner(){
        return (map1, map2) -> {
            map1.putAll(map2);
            return map1;
        };
    }

    @Override
    public Function<Map<Integer, Boolean>, Map<Integer, Boolean>> finisher(){
        return map -> map;
    }

    @Override
    public Set<Characteristics> characteristics(){
        return EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED);
    }
}

然后最终可以这样调用:

Map<Integer, Boolean> map = mappings.stream().collect(new AToMapCollector());

创建一个全新类的增加是这个收集器的可重用性,并且增加了可读性而不是多行lambda。