Java静态构建类似于IntStream。

huangapple go评论70阅读模式
英文:

Java static build similar to IntStream

问题

以下是翻译好的内容:

我的目标是提供一个类,用于构建一个函数链,以便在之后执行某些任务。以下是我目前想出来的内容:

函数类

public class Loop {
    private final int from;
    private final int to;

    private Loop(int from, int to) {
        this.from = from;
        this.to = to;
    }

    public static Loop from(int from) {
        return new Loop(from, 0);
    }

    public static Loop to(int to) {
        return new Loop(0, to);
    }

    public void execute(Executable executable) {
        for (int i = from; i < to; i++) {
            executable.execute();
        }
    }
}

可执行接口

@FunctionalInterface
public interface Executable {
    void execute();
}

它可以适用于一个参数(from 或 to),例如:

Loop.to(10).execute(() -> {});

但我希望它也可以适用于多个参数,例如:

Loop.from(5).to(10).execute(() -> {});

我该如何实现这一点?而且我不确定静态的 from 和 to 方法是否适合 Loop 类,因为它们有一个多余的参数。

英文:

My aim is to provide a class to build a chain of functions to execute some task after. Here is what I could come up with so far

Function class

public class Loop {
    private final int from;
    private final int to;

    private Loop(int from, int to) {
        this.from = from;
        this.to = to;
    }

    public static Loop from(int from) {
        return new Loop(from, 0);
    }

    public static Loop to(int to) {
        return new Loop(0, to);
    }

    public void execute(Executable executable) {
        for (int i = from; i &lt; to; i++) {
            executable.execute();
        }
    }
}

Executable interface

@FunctionalInterface
public interface Executable {
    void execute();
}

It can work for one parameter (from or to) like

Loop.to(10).execute(() -&gt; {});

but I want it to work for multiple parameters like

Loop.from(5).to(10).execute(() -&gt; {});

How can I achieve that? Also I am not sure if static from and static to methods fit in Loop class well with one redundant parameter.

答案1

得分: 1

以下是翻译好的内容:

理想情况下,会有from的静态变体和相同名称的非静态变体,但是不支持这样做。要么参数不同,要么名称不同。

因此,我会将私有构造函数更改为公共构造函数,并添加第二个无参数构造函数,以便调用可以变为:

new Loop().from(5).to(10).execute(...)
new Loop(5, 10).execute(...)

为了使其正常工作,fromto不能再是静态的。然后,您需要决定这些方法是修改调用它们的Loop实例还是返回一个新对象:

public Loop from(int from) {
    return new Loop(from, to);
}

public Loop to(int to) {
    return new Loop(from, to);
}

或者

public Loop from(int from) {
    this.from = from; // 不再是final
    return this;
}

public Loop to(int to) {
    this.to = to; // 不再是final
    return this;
}

如果您希望进一步发展,可以将类重命名为LoopBuilder,并创建一个返回具有与之前相同字段但没有fromto方法的Loopbuild()方法:

public static class Loop {

    private final int from;
    private final int to;

    public Loop(int from, int to) {
        this.from = from;
        this.to = to;
    }

    public void execute(Executable executable) {
        for (int i = from; i < to; i++) {
            executable.execute();
        }
    }

    public static LoopBuilder builder() {
        return new LoopBuilder();
    }
}

public static class LoopBuilder {
    private int from;
    private int to;

    public LoopBuilder from(int from) {
        this.from = from;
        return this;
    }

    public LoopBuilder to(int to) {
        this.to = to;
        return this;
    }

    public Loop build() {
        return new Loop(from, to);
    }
}

调用将变为:

Loop.builder().from(5).to(10).build().execute(...);
英文:

It would be ideal to have static variant of from and non-static one of the same name, but that is not supported. Either the arguments or the name has to differ.

Therefore I would make the private constructor public and add a second no-args constructor so that the call could become:

new Loop().from(5).to(10).execute(...)
new Loop(5, 10).execute(...)

For that to work the from and to must no longer be static. Then you have to decide if the methods mutate the Loop instance they are invoked on or if they return a new object:

public Loop from(int from) {
    return new Loop(from, to);
}

public Loop to(int to) {
    return new Loop(from, to);
}

or

public Loop from(int from) {
    this.from = from; // no longer final
    return this;
}

public Loop to(int to) {
    this.to = to; // no longer final
    return this;
}

If you want to take this further you can rename the class to LoopBuilder and create a build() method that returns a Loop that has the fields as before, but no from and to methods, just the execute method:

public static class Loop {

    private final int from;
    private final int to;

    public Loop(int from, int to) {
        this.from = from;
        this.to = to;
    }

    public void execute(Executable executable) {
        for (int i = from; i &lt; to; i++) {
            executable.execute();
        }
    }
    
    public static LoopBuilder builder() {
        return new LoopBuilder();
    }
}

public static class LoopBuilder {
    private int from;
    private int to;

    public LoopBuilder from(int from) {
        this.from = from;
        return this;
    }

    public LoopBuilder to(int to) {
        this.to = to;
        return this;
    }

    public Loop build() {
        return new Loop(from, to);
    }
}

The call would then become

Loop.builder().from(5).to(10).build().execute(...);

答案2

得分: 0

以下是翻译好的内容:

有几种方法可以采取。解决方案越复杂,通常就越复杂。让我们从一个简单的解决方案开始。

public class Loop {
    private final int from;
    private final int to;

    private Loop(Builder builder) {
        this.from = builder.from();
        this.to = builder.to();
    }

    public static Builder from(int from) {
        return new Builder().from(from);
    }

    public static Builder to(int to) {
        return new Builder().to(to);
    }

    public void execute(Runnable executable) {
        for (int i = from; i < to; i++) {
            executable.run();
        }
    }

    public static class Builder {
        private int from = 0;
        private Integer to = null;

        private Builder() {}

        public Builder from(int from) {
            this.from = from;
            return this;
        }

        private int from() {
            return from;
        }

        public Builder to(int to) {
            this.to = to;
            return this;
        }

        private int to() {
            return to;
        }

        public void execute(Runnable runnable) {
            Objects.requireNonNull(runnable);
            new Loop(this).execute(runnable);
        }
    }
}

这已经相当不错了。但是,例如,我们可以多次调用 from(...)to(...)。如果我们只允许对 from(...) 进行一次调用,之后只允许对 to(...) 进行调用,那么我们需要定义更多的类型。我们可以通过添加接口 LoopFromSetBuilderLoopToSetBuilderLoopAllSetBuilder 来实现这一点:

interface LoopFromSetBuilder {
    LoopAllSetBuilder to(int to);
}

interface LoopToSetBuilder {
    LoopAllSetBuilder from(int from);
}

interface LoopAllSetBuilder {
    void execute(Runnable runnable);
}

再加上对方法 Loop 进行微小调整:

class Loop {
    ...
    public static LoopFromSetBuilder from(int from) {
        return new Builder().from(from);
    }

    public static LoopToSetBuilder to(int to) {
        return new Builder().to(to);
    }
    ...
}

并且让 Builder 实现这些接口:

public static class Builder implements LoopFromSetBuilder, LoopToSetBuilder, LoopAllSetBuilder {
    ...
}

我们消除了用户多次调用 from(...)to(...) 的可能性。

我没有更多关于构建这些“复杂”流畅API的经验,但我想象构建这样的API的过程可能会变得非常痛苦。对于小的示例,就像这里一样,我们也许可以阻止多次设置相同的变量,但是在有更多参数的情况下似乎会变得相当混乱(可能的状态 - 因此 - 接口的数量似乎是以 2^n 的顺序增长,其中 n 是字段的数量... 是的)。

如果有人赞同我的回答,也应该考虑赞同 Luk2302 的回答,因为我的答案的第一个解决方案与 Luk2302 的解决方案非常相似,而且 Luk2302 的答案比我的略早发布。

英文:

There are several approach one can take. The more sophisticated the solutions get, the more complex they get normally. Let us start with a simple solution.

public class Loop {
private final int from;
private final int to;
private Loop(Builder builder) {
this.from = builder.from();
this.to = builder.to();
}
public static Builder from(int from) {
return new Builder().from(from);
}
public static Builder to(int to) {
return new Builder().to(to);
}
public void execute(Runnable executable) {
for (int i = from; i &lt; to; i++) {
executable.run();
}
}
public static class Builder {
private int from = 0;
private Integer to = null;
private Builder() {}
public Builder from(int from) {
this.from = from;
return this;
}
private int from() {
return from;
}
public Builder to(int to) {
this.to = to;
return this;
}
private int to() {
return to;
}
public void execute(Runnable runnable) {
Objects.requireNonNull(runnable);
new Loop(this).execute(runnable);
}
}
}

<kbd>Ideone demo</kbd>

This is already quite something. But we can, for example, call from(...) or to(...) multiple times. If we were to only allow one call to from(...) and after this only allow calls to to(...), then we need to define more types. We could achieve this by adding interfaces LoopFromSetBuilder, LoopToSetBuilder and LoopAllSetBuilder:

interface LoopFromSetBuilder {
LoopAllSetBuilder to(int to);
}
interface LoopToSetBuilder {
LoopAllSetBuilder from(int from);
}
interface LoopAllSetBuilder {
void execute(Runnable runnable);
}

Together with a little tweak in method Loop

class Loop {
...
public static LoopFromSetBuilder from(int from) {
return new Builder().from(from);
}
public static LoopToSetBuilder to(int to) {
return new Builder().to(to);
}
...
}

and letting Builder implement those interfaces:

    public static class Builder implements LoopFromSetBuilder, LoopToSetBuilder, LoopAllSetBuilder {
...
}

<kbd>Ideone demo</kbd>

We elimiate the possibility for the user to call from(...) and to(...) multiple times.

I have no further experience of building those "complex" fluent APIs, but I imagine that the process of building such an API can get quite painful. For small examples, as here, we might be able to deny setting the same variable mutliple times, but it seems pretty messy with more parameters (possible states - and thus - interfaces seem to be in the order of 2^n where n is the number of fields... so yeah.

If one upvotes my answer, one should also consider upvoting Luk2302's answer since the first solution of my answer is very similar with Luk2302's solution and Luk2302's answer was posted a little bit earlier than mine.

huangapple
  • 本文由 发表于 2020年8月22日 23:52:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/63538150.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定