Как я понимаю, вы хотели бы следующее:
новый "ключ" даст класс Target с другой стороной, равной нулю
Если оставить или правая сторона выдает НОВЫЙ ключ (пред .: ноль или другой)
, а затем однажды другая сторона также выдает тот же ключ соединения,
предварительное условие: поток испущен значение - другой поток теперь выдает значение и ключ для левого и правого эквалайзера
последние с обеих сторон выдаются всякий раз, когда есть новое значение в любой последовательности для данного ключа.
испускать полную цель (составленную из левого, правого) каждого левого, правого испускания, когда значение left, right заметно меняется
RxJava2 решение для моего предположения:
@Test
void test2() {
PublishSubject<Foo> foo$ = PublishSubject.create();
PublishSubject<Bar> bar$ = PublishSubject.create();
Observable<Target> target$ = Observable.merge(Arrays.asList(foo$, bar$))
// filter invalid values
.filter(hasId -> hasId.key() != null)
.scan(Target.NULL, (prev, change) -> {
// when prev. target and current value#key are eq -> emit composed value
if (change.key().equals(prev.key)) {
return composedTarget(prev, change);
} else if (change instanceof Foo) {
return Target.fromFoo((Foo) change);
} else if (change instanceof Bar) {
return Target.fromBar((Bar) change);
}
return prev;
}).filter(target -> target != Target.NULL)
.distinctUntilChanged();
TestObserver<Target> test = target$.test();
// emit
foo$.onNext(new Foo("123", "f1"));
// emit
bar$.onNext(new Bar("123", "f2"));
// emit
bar$.onNext(new Bar("123", "f3"));
// skipped
foo$.onNext(new Foo("123", "f1"));
// emit
foo$.onNext(new Foo("123", "f5"));
// emit
foo$.onNext(new Foo("key", "value"));
// emit
foo$.onNext(new Foo("key2", "value2"));
// emit
bar$.onNext(new Bar("bar2", "Berlin"));
// emit
foo$.onNext(new Foo("foo2", "Funkeey"));
test.assertValues(
new Target("123", "f1", null),
new Target("123", "f1", "f2"),
new Target("123", "f1", "f3"),
new Target("123", "f5", "f3"),
new Target("key", "value", null),
new Target("key2", "value2", null),
new Target("bar2", null, "Berlin"),
new Target("foo2", "Funkeey", null)
);
}
private Target composedTarget(Target prev, HasId change) {
if (change instanceof Foo) {
Foo foo = (Foo) change;
return new Target(prev.key, foo.funky, prev.town);
}
if (change instanceof Bar) {
Bar bar = (Bar) change;
return new Target(prev.key, prev.funky, bar.town);
}
return prev;
}
Domain-Classes
interface HasId {
String key();
}
static final class Foo implements HasId {
final String key;
final String funky;
Foo(String key, String funky) {
this.key = key;
this.funky = funky;
}
@Override
public String key() {
return key;
}
}
static final class Bar implements HasId {
String key;
String town;
Bar(String key, String town) {
this.key = key;
this.town = town;
}
@Override
public String key() {
return key;
}
}
static final class Target {
private static final Target NULL = new Target(null, null, null);
final String key;
final String funky;
final String town;
Target(String key, String funky, String town) {
this.key = key;
this.funky = funky;
this.town = town;
}
static Target fromFoo(Foo foo) {
return new Target(foo.key, foo.funky, null);
}
static Target fromBar(Bar bar) {
return new Target(bar.key, null, bar.town);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Target target = (Target) o;
return key.equals(target.key) &&
Objects.equals(funky, target.funky) &&
Objects.equals(town, target.town);
}
@Override
public int hashCode() {
return Objects.hash(key, funky, town);
}
@Override
public String toString() {
return "Target{" +
"key='" + key + '\'' +
", funky='" + funky + '\'' +
", town='" + town + '\'' +
'}';
}
}
Пожалуйста, исправьте мои предположения, если я ошибаюсь. Решение может быть реализовано лучше в C# с сопоставлением с шаблоном. На самом деле, если C# имеет типы объединения, такие как F #, это было бы лучше.