Skip to content

Commit 8c85f5a

Browse files
authored
3.x: Add Maybe/Completable toFuture (#6875)
* 3.x: Add Maybe/Completable toFuture * Fix copy-paste mistakes
1 parent d5449b2 commit 8c85f5a

File tree

8 files changed

+224
-7
lines changed

8 files changed

+224
-7
lines changed

src/main/java/io/reactivex/rxjava3/core/Completable.java

+22
Original file line numberDiff line numberDiff line change
@@ -2892,6 +2892,28 @@ public final <T> Flowable<T> toFlowable() {
28922892
}
28932893
return RxJavaPlugins.onAssembly(new CompletableToFlowable<>(this));
28942894
}
2895+
/**
2896+
* Returns a {@link Future} representing the termination of the current {@code Completable}
2897+
* via a {@code null} value.
2898+
* <p>
2899+
* <img width="640" height="433" src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Completable.toFuture.png" alt="">
2900+
* <p>
2901+
* Cancelling the {@code Future} will cancel the subscription to the current {@code Completable}.
2902+
* <dl>
2903+
* <dt><b>Scheduler:</b></dt>
2904+
* <dd>{@code toFuture} does not operate by default on a particular {@link Scheduler}.</dd>
2905+
* </dl>
2906+
*
2907+
* @return the new {@code Future} instance
2908+
* @see <a href="http://reactivex.io/documentation/operators/to.html">ReactiveX documentation: To</a>
2909+
* @since 3.0.0
2910+
*/
2911+
@CheckReturnValue
2912+
@SchedulerSupport(SchedulerSupport.NONE)
2913+
@NonNull
2914+
public final Future<Void> toFuture() {
2915+
return subscribeWith(new FutureMultiObserver<>());
2916+
}
28952917

28962918
/**
28972919
* Converts this {@code Completable} into a {@link Maybe}.

src/main/java/io/reactivex/rxjava3/core/Flowable.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -6249,7 +6249,7 @@ public final T blockingSingle(@NonNull T defaultItem) {
62496249
/**
62506250
* Returns a {@link Future} representing the only value emitted by this {@code Flowable}.
62516251
* <p>
6252-
* <img width="640" height="324" src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Flowable.toFuture.png" alt="">
6252+
* <img width="640" height="311" src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Flowable.toFuture.png" alt="">
62536253
* <p>
62546254
* If the {@code Flowable} emits more than one item, {@link java.util.concurrent.Future} will receive an
62556255
* {@link java.lang.IndexOutOfBoundsException}. If the {@code Flowable} is empty, {@link java.util.concurrent.Future}

src/main/java/io/reactivex/rxjava3/core/Maybe.java

+23
Original file line numberDiff line numberDiff line change
@@ -4166,6 +4166,29 @@ public final Flowable<T> toFlowable() {
41664166
return RxJavaPlugins.onAssembly(new MaybeToFlowable<>(this));
41674167
}
41684168

4169+
/**
4170+
* Returns a {@link Future} representing the single value emitted by the current {@code Maybe}
4171+
* or {@code null} if the current {@code Maybe} is empty.
4172+
* <p>
4173+
* <img width="640" height="292" src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Maybe.toFuture.png" alt="">
4174+
* <p>
4175+
* Cancelling the {@code Future} will cancel the subscription to the current {@code Maybe}.
4176+
* <dl>
4177+
* <dt><b>Scheduler:</b></dt>
4178+
* <dd>{@code toFuture} does not operate by default on a particular {@link Scheduler}.</dd>
4179+
* </dl>
4180+
*
4181+
* @return the new {@code Future} instance
4182+
* @see <a href="http://reactivex.io/documentation/operators/to.html">ReactiveX documentation: To</a>
4183+
* @since 3.0.0
4184+
*/
4185+
@CheckReturnValue
4186+
@SchedulerSupport(SchedulerSupport.NONE)
4187+
@NonNull
4188+
public final Future<T> toFuture() {
4189+
return subscribeWith(new FutureMultiObserver<>());
4190+
}
4191+
41694192
/**
41704193
* Converts this {@code Maybe} into an {@link Observable} instance composing disposal
41714194
* through.

src/main/java/io/reactivex/rxjava3/core/Observable.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -5654,7 +5654,7 @@ public final T blockingSingle(@NonNull T defaultItem) {
56545654
/**
56555655
* Returns a {@link Future} representing the only value emitted by the current {@code Observable}.
56565656
* <p>
5657-
* <img width="640" height="312" src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/toFuture.o.png" alt="">
5657+
* <img width="640" height="299" src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/toFuture.o.png" alt="">
56585658
* <p>
56595659
* If the {@code Observable} emits more than one item, {@code Future} will receive an
56605660
* {@link IndexOutOfBoundsException}. If the {@code Observable} is empty, {@code Future}

src/main/java/io/reactivex/rxjava3/core/Single.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -4625,6 +4625,8 @@ public final Flowable<T> toFlowable() {
46254625
* Returns a {@link Future} representing the single value emitted by this {@code Single}.
46264626
* <p>
46274627
* <img width="640" height="467" src="https://github.com/ReactiveX/RxJava/wiki/images/rx-operators/Single.toFuture.png" alt="">
4628+
* <p>
4629+
* Cancelling the {@code Future} will cancel the subscription to the current {@code Single}.
46284630
* <dl>
46294631
* <dt><b>Scheduler:</b></dt>
46304632
* <dd>{@code toFuture} does not operate by default on a particular {@link Scheduler}.</dd>
@@ -4637,7 +4639,7 @@ public final Flowable<T> toFlowable() {
46374639
@SchedulerSupport(SchedulerSupport.NONE)
46384640
@NonNull
46394641
public final Future<T> toFuture() {
4640-
return subscribeWith(new FutureSingleObserver<>());
4642+
return subscribeWith(new FutureMultiObserver<>());
46414643
}
46424644

46434645
/**

src/main/java/io/reactivex/rxjava3/internal/observers/FutureSingleObserver.java src/main/java/io/reactivex/rxjava3/internal/observers/FutureMultiObserver.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.util.concurrent.atomic.AtomicReference;
2020

2121
import io.reactivex.rxjava3.annotations.NonNull;
22-
import io.reactivex.rxjava3.core.SingleObserver;
22+
import io.reactivex.rxjava3.core.*;
2323
import io.reactivex.rxjava3.disposables.Disposable;
2424
import io.reactivex.rxjava3.internal.disposables.DisposableHelper;
2525
import io.reactivex.rxjava3.internal.util.BlockingHelper;
@@ -31,15 +31,15 @@
3131
*
3232
* @param <T> the value type
3333
*/
34-
public final class FutureSingleObserver<T> extends CountDownLatch
35-
implements SingleObserver<T>, Future<T>, Disposable {
34+
public final class FutureMultiObserver<T> extends CountDownLatch
35+
implements MaybeObserver<T>, SingleObserver<T>, CompletableObserver, Future<T>, Disposable {
3636

3737
T value;
3838
Throwable error;
3939

4040
final AtomicReference<Disposable> upstream;
4141

42-
public FutureSingleObserver() {
42+
public FutureMultiObserver() {
4343
super(1);
4444
this.upstream = new AtomicReference<>();
4545
}
@@ -141,6 +141,16 @@ public void onError(Throwable t) {
141141
}
142142
}
143143

144+
@Override
145+
public void onComplete() {
146+
Disposable a = upstream.get();
147+
if (a == DisposableHelper.DISPOSED) {
148+
return;
149+
}
150+
upstream.compareAndSet(a, this);
151+
countDown();
152+
}
153+
144154
@Override
145155
public void dispose() {
146156
// ignoring as `this` means a finished Disposable only
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* Copyright (c) 2016-present, RxJava Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
5+
* compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is
10+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
11+
* the License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package io.reactivex.rxjava3.internal.operators.completable;
15+
16+
import static org.junit.Assert.*;
17+
18+
import java.util.concurrent.*;
19+
20+
import org.junit.Test;
21+
22+
import io.reactivex.rxjava3.core.*;
23+
import io.reactivex.rxjava3.exceptions.TestException;
24+
import io.reactivex.rxjava3.schedulers.Schedulers;
25+
import io.reactivex.rxjava3.subjects.CompletableSubject;
26+
27+
public class CompletableToFutureTest extends RxJavaTest {
28+
29+
@Test
30+
public void empty() throws Exception {
31+
assertNull(Completable.complete()
32+
.subscribeOn(Schedulers.computation())
33+
.toFuture()
34+
.get());
35+
}
36+
37+
@Test
38+
public void error() throws InterruptedException {
39+
try {
40+
Completable.error(new TestException())
41+
.subscribeOn(Schedulers.computation())
42+
.toFuture()
43+
.get();
44+
45+
fail("Should have thrown!");
46+
} catch (ExecutionException ex) {
47+
assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException);
48+
}
49+
}
50+
51+
@Test
52+
public void cancel() {
53+
CompletableSubject cs = CompletableSubject.create();
54+
55+
Future<Void> f = cs.toFuture();
56+
57+
assertTrue(cs.hasObservers());
58+
59+
f.cancel(true);
60+
61+
assertFalse(cs.hasObservers());
62+
}
63+
64+
@Test
65+
public void cancel2() {
66+
CompletableSubject cs = CompletableSubject.create();
67+
68+
Future<Void> f = cs.toFuture();
69+
70+
assertTrue(cs.hasObservers());
71+
72+
f.cancel(false);
73+
74+
assertFalse(cs.hasObservers());
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Copyright (c) 2016-present, RxJava Contributors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
5+
* compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is
10+
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See
11+
* the License for the specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package io.reactivex.rxjava3.internal.operators.maybe;
15+
16+
import static org.junit.Assert.*;
17+
18+
import java.util.concurrent.*;
19+
20+
import org.junit.Test;
21+
22+
import io.reactivex.rxjava3.core.*;
23+
import io.reactivex.rxjava3.exceptions.TestException;
24+
import io.reactivex.rxjava3.schedulers.Schedulers;
25+
import io.reactivex.rxjava3.subjects.MaybeSubject;
26+
27+
public class MaybeToFutureTest extends RxJavaTest {
28+
29+
@Test
30+
public void success() throws Exception {
31+
assertEquals((Integer)1, Maybe.just(1)
32+
.subscribeOn(Schedulers.computation())
33+
.toFuture()
34+
.get());
35+
}
36+
37+
@Test
38+
public void empty() throws Exception {
39+
assertNull(Maybe.empty()
40+
.subscribeOn(Schedulers.computation())
41+
.toFuture()
42+
.get());
43+
}
44+
45+
@Test
46+
public void error() throws InterruptedException {
47+
try {
48+
Maybe.error(new TestException())
49+
.subscribeOn(Schedulers.computation())
50+
.toFuture()
51+
.get();
52+
53+
fail("Should have thrown!");
54+
} catch (ExecutionException ex) {
55+
assertTrue("" + ex.getCause(), ex.getCause() instanceof TestException);
56+
}
57+
}
58+
59+
@Test
60+
public void cancel() {
61+
MaybeSubject<Integer> ms = MaybeSubject.create();
62+
63+
Future<Integer> f = ms.toFuture();
64+
65+
assertTrue(ms.hasObservers());
66+
67+
f.cancel(true);
68+
69+
assertFalse(ms.hasObservers());
70+
}
71+
72+
@Test
73+
public void cancel2() {
74+
MaybeSubject<Integer> ms = MaybeSubject.create();
75+
76+
Future<Integer> f = ms.toFuture();
77+
78+
assertTrue(ms.hasObservers());
79+
80+
f.cancel(false);
81+
82+
assertFalse(ms.hasObservers());
83+
}
84+
}

0 commit comments

Comments
 (0)