Skip to content

Commit d56959d

Browse files
Merge pull request #1337 from zsxwing/fix-toFuture
Make Future receive NoSuchElementException when the BlockingObservable is empty
2 parents f7de6e9 + 8d06589 commit d56959d

File tree

4 files changed

+38
-13
lines changed

4 files changed

+38
-13
lines changed

rxjava-core/src/main/java/rx/internal/operators/BlockingOperatorToFuture.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public static <T> Future<T> toFuture(Observable<? extends T> that) {
5252
final AtomicReference<T> value = new AtomicReference<T>();
5353
final AtomicReference<Throwable> error = new AtomicReference<Throwable>();
5454

55-
final Subscription s = that.subscribe(new Subscriber<T>() {
55+
final Subscription s = that.single().subscribe(new Subscriber<T>() {
5656

5757
@Override
5858
public void onCompleted() {
@@ -67,11 +67,8 @@ public void onError(Throwable e) {
6767

6868
@Override
6969
public void onNext(T v) {
70-
if (!value.compareAndSet(null, v)) {
71-
// this means we received more than one value and must fail as a Future can handle only a single value
72-
error.compareAndSet(null, new IllegalStateException("Observable.toFuture() only supports sequences with a single value. Use .toList().toFuture() if multiple values are expected."));
73-
finished.countDown();
74-
}
70+
// "single" guarantees there is only one "onNext"
71+
value.set(v);
7572
}
7673
});
7774

rxjava-core/src/main/java/rx/internal/operators/OperatorSingle.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public void onNext(T value) {
5656
if (isNonEmpty) {
5757
hasTooManyElements = true;
5858
subscriber.onError(new IllegalArgumentException("Sequence contains too many elements"));
59+
unsubscribe();
5960
} else {
6061
this.value = value;
6162
isNonEmpty = true;

rxjava-core/src/main/java/rx/observables/BlockingObservable.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,11 @@ public T singleOrDefault(T defaultValue, Func1<? super T, Boolean> predicate) {
405405
/**
406406
* Returns a {@link Future} representing the single value emitted by this {@code BlockingObservable}.
407407
* <p>
408-
* {@code toFuture} throws an exception if the {@code BlockingObservable} emits more than one item. If the
409-
* {@code BlockingObservable} may emit more than item, use
410-
* {@link Observable#toList toList()}{@code .toFuture()}.
408+
* If {@link BlockingObservable} emits more than one item, {@link java.util.concurrent.Future} will receive an
409+
* {@link java.lang.IllegalArgumentException}. If {@link BlockingObservable} is empty, {@link java.util.concurrent.Future}
410+
* will receive an {@link java.util.NoSuchElementException}.
411+
* <p>
412+
* If the {@code BlockingObservable} may emit more than one item, use {@code Observable.toList().toBlocking().toFuture()}.
411413
* <p>
412414
* <img width="640" height="395" src="https://github.com/Netflix/RxJava/wiki/images/rx-operators/B.toFuture.png">
413415
*

rxjava-core/src/test/java/rx/internal/operators/BlockingOperatorToFutureTest.java

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static rx.internal.operators.BlockingOperatorToFuture.toFuture;
2222

2323
import java.util.List;
24+
import java.util.NoSuchElementException;
2425
import java.util.concurrent.CancellationException;
2526
import java.util.concurrent.ExecutionException;
2627
import java.util.concurrent.Future;
@@ -51,12 +52,17 @@ public void testToFutureList() throws InterruptedException, ExecutionException {
5152
assertEquals("three", f.get().get(2));
5253
}
5354

54-
@Test(expected = ExecutionException.class)
55-
public void testExceptionWithMoreThanOneElement() throws InterruptedException, ExecutionException {
55+
@Test(expected = IllegalArgumentException.class)
56+
public void testExceptionWithMoreThanOneElement() throws Throwable {
5657
Observable<String> obs = Observable.from("one", "two");
5758
Future<String> f = toFuture(obs);
58-
assertEquals("one", f.get());
59-
// we expect an exception since there are more than 1 element
59+
try {
60+
// we expect an exception since there are more than 1 element
61+
f.get();
62+
}
63+
catch(ExecutionException e) {
64+
throw e.getCause();
65+
}
6066
}
6167

6268
@Test
@@ -106,4 +112,23 @@ public void call(Subscriber<? super T> unused) {
106112
// do nothing
107113
}
108114
}
115+
116+
@Test(expected = NoSuchElementException.class)
117+
public void testGetWithEmptyObservable() throws Throwable {
118+
Observable<String> obs = Observable.empty();
119+
Future<String> f = obs.toBlocking().toFuture();
120+
try {
121+
f.get();
122+
}
123+
catch(ExecutionException e) {
124+
throw e.getCause();
125+
}
126+
}
127+
128+
@Test
129+
public void testGetWithASingleNullItem() throws Exception {
130+
Observable<String> obs = Observable.from((String)null);
131+
Future<String> f = obs.toBlocking().toFuture();
132+
assertEquals(null, f.get());
133+
}
109134
}

0 commit comments

Comments
 (0)