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

Java 将具有lambda的类映射到默认值,java,lambda,java-8,Java,Lambda,Java 8,我的后续问题。 有这样的等级制度。其中A是基类: A / \ B C | A | | B | | C | | getId()| |A.getId() | |A.getId()| |isVisible()| 以及以下内容: List<A> mappings; 丑陋的版本是: mappings.stream()

我的后续问题。 有这样的等级制度。其中A是基类:

       A 
      / \
     B   C

  |   A    |   |   B       |   |  C      |  
  | getId()|   |A.getId()  |   |A.getId()|
               |isVisible()| 
以及以下内容:

List<A> mappings;
丑陋的版本是:

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

有没有关于改进它以提供更优雅版本的默认值的帮助?

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

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()));

我编写了这个简单的
收集器
实现,它可以实现您想要的功能:

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);
    }
}
公共类AToMapCollector实现收集器{
@凌驾
公共供应商(){
返回HashMap::new;
}
@凌驾
公共双消费者累加器(){
返回(地图,a)->{
布尔可见=真;
if(B的a实例){
可见=((B)a).isVisible();
}
put(a.getId(),可见);
};
}
@凌驾
公共二进制运算符组合器(){
返回(map1、map2)->{
map1.putAll(map2);
返回map1;
};
}
@凌驾
公共函数完成器(){
返回地图->地图;
}
@凌驾
公共集特征(){
返回枚举集(Characteristics.IDENTITY\u FINISH,Characteristics.UNORDERED);
}
}
最后可以这样称呼:

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

添加创建一个全新的类是该收集器的可重用性,同时也提高了可读性,而不是多行lambda。

也许您可以使用helper类执行您想要的操作:

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));
然后,要知道某个元素是否可见,只需检查它是否属于集合:

boolean isVisible = set.contains(elementId);

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

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

如果无法更改源代码,则可以编写实用程序方法
isA
来描述所需内容,例如:

Map<Integer, Boolean> visibility = mappings.stream().collect(toMap(
        A::getId,
        isA(B.class, B::isVisible, any -> true)
));
Map visibility=mappings.stream().collect(toMap(
A::getId,
isA(B.class,B::isVisible,any->true)
));

静态函数isA(类您的变体

mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m ->
                        {
                            boolean isB = m instanceof B;
                            return isB ? ((B) m).isVisible() : true;
                        }));
这不是很难看吗,因为它表达了你的意图

但是您可以简化它,因为您不需要局部变量来保存B的
m实例

mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m->m instanceof B? ((B)m).isVisible(): true));
然后,根据经验,只要在复合布尔表达式中有
布尔值
文本,就有一种没有它的替代方法

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


类A包含
getId
方法,还是我错了?@OlimpiuPOP那么你为什么调用
B::getId
?你的例子有点让人困惑。在上一个lambda中,你命名了变量
m
,但你使用的是
映射实例B
。请检查你的例子以避免与你的问题无关的问题。还有de>.filter(a->instanceof B)
可能应该是
.filter(a->a instanceof B)
您应该能够减少
m->{boolean isB=m instanceof B;返回isB?((B)m)。isVisible():true;}
m->m instanceof B?((B)m)。isVisible():true
(尽管还不清楚这是否是您想要的).说真的-你所拥有的并不丑陋。你不会得到更好的建议。Pshemo是对的-这是你应该做的唯一一件事,以使你的代码更好…还有,这是如何比一个更简单的lambda表达式更好?我没有看到这一点的好处…我是从最初的问题出发的,而不是从现在提供的评论出发的一个简单的lambda表达式是的,我也这么认为,但我不能改变层次结构。谢谢首先,起来!我们是一样的。但是我用了一个实用方法来代替。嗨,梁!很好的方法,非常好functional@FedericoPeraltaSchaffner谢谢,你的回答激发了我的灵感。@FedericoPeraltaSchaffner你的记忆力很好。谢谢你记住我,我的朋友。:)请接受Federicoperalthaffner的答案,我的答案是从他的答案中得到启发的。但是你的答案要好得多,不需要帮助类
m==null | | m.isVisible()
.map(a->a instanceof C?(B)null:(B)
应该是
.map(a->a instanceof B?(B)a:(B)null)
还是我错了?@Eugene:也许值得发布一个包含所有组合的完整表格以供演示…@Eugene:不,不必要的复杂布尔表达式是一个反复出现的东西,不仅仅是在Stackoverflow上,所以我认为,这样一个表格,提醒开发人员更简单的替代方案,并证明我的特殊规则有效,也就是说,如果您在任何其他上下文中看到
true
false
而不是作为唯一常量,那么它应该总是会响起来,这可能有一些价值。好的,我同意。但那应该是一个单独的问答。。。如果你发布这样的帖子,我会更乐意投票——很可能甚至会将其导入我们的代码中base@Holger如果我要根据你的经验法则来实现声纳或FunBug的警报,我也会考虑常量谓词有效,即<代码> T->真< /代码>,我的意思是,除了
boolean variable=true
@Federico Peralta Schaffner:这就是为什么我说“只要在复合布尔表达式中有布尔文字”。一个单独的文字不算数。简化规则甚至可以计算为布尔文字,例如
a&&false
→ <代码>错误…
mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m ->
                        {
                            boolean isB = m instanceof B;
                            return isB ? ((B) m).isVisible() : true;
                        }));
mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m->m instanceof B? ((B)m).isVisible(): true));
mappings.stream()                       
        .collect(Collectors.toMap(A::getId, m -> !(m instanceof B) || ((B)m).isVisible()));