답변:
내가 아는 두 가지 옵션 은 2007의 Aviad Ben Dov의 infomancers-collections 라이브러리 와 2008의 Jim Blackler의 YieldAdapter 라이브러리입니다 (다른 답변에서도 언급 됨).
둘 다 yield return
Java에서 -like 구조로 코드를 작성할 수 있으므로 둘 다 요청을 충족시킬 것입니다. 둘 사이의 주목할만한 차이점은 다음과 같습니다.
Aviad의 라이브러리는 바이트 코드 조작을 사용하고 Jim은 멀티 스레딩을 사용합니다. 필요에 따라 각각의 장점과 단점이있을 수 있습니다. Aviad의 솔루션은 더 빠르지 만 Jim은 더 이식성이 뛰어납니다 (예를 들어, Aviad의 라이브러리가 Android에서 작동하지 않을 것이라고 생각합니다).
Aviad의 라이브러리에는 더 깔끔한 인터페이스가 있습니다. 예를 들면 다음과 같습니다.
Iterable<Integer> it = new Yielder<Integer>() {
@Override protected void yieldNextCore() {
for (int i = 0; i < 10; i++) {
yieldReturn(i);
if (i == 5) yieldBreak();
}
}
};
Jim 's는 훨씬 더 복잡하지만 방법이 adept
있는 제네릭 Collector
을 요구합니다 collect(ResultHandler)
. ugh. 그러나 Zoom Information에 의한 Jim의 코드 주위에 다음과 같은 것을 사용할 수 있습니다 .
Iterable<Integer> it = new Generator<Integer>() {
@Override protected void run() {
for (int i = 0; i < 10; i++) {
yield(i);
if (i == 5) return;
}
}
};
Aviad의 솔루션은 BSD입니다.
Jim의 솔루션은 공개 도메인이며 위에서 언급 한 래퍼도 마찬가지입니다.
AbstractIterator
.
yield(i)
때문에, JDK (13)로 중단됩니다 yield
새로운 자바 문의 / 예약 키워드로 추가되고있다.
이제 Java에는 Lambda가 있습니다. 다음과 같이 할 수 있습니다.
public Yielderable<Integer> oneToFive() {
return yield -> {
for (int i = 1; i < 10; i++) {
if (i == 6) yield.breaking();
yield.returning(i);
}
};
}
Yielderable
? 그냥 그래야하지 Yieldable
않나요? (동사는하지 'yielder'또는 'yielderate'또는 무엇이든 단지 '수율'인)
yield -> { ... }
yield
새 Java 문 / 예약 키워드로 추가 되므로 JDK 13부터 중단됩니다 .
나는 이것이 매우 오래된 질문이라는 것을 알고 있으며 위에서 설명한 두 가지 방법이 있습니다.
yield
분명히 리소스 비용이 드는 스레드 기반 .그러나 yield
C # 2.0+ 컴파일러가 yield return/break
생성을 위해 수행하는 작업에 가장 가까운 구현 인 자바 에서 생성기 를 구현하는 세 번째이자 아마도 가장 자연스러운 방법 인 lombok-pg가 있습니다. 완전히 상태 머신을 기반으로하며 javac
소스 코드 AST를 조작하기 위해 긴밀한 협력이 필요합니다 . 안타깝게도 lombok-pg 지원은 중단 된 것으로 보이며 (1 ~ 2 년 이상 리포지토리 활동 없음), 원래 프로젝트 Lombok 에는 안타깝게도이yield
기능 이 없습니다 (이클립스, IntelliJ IDEA 지원과 같은 더 나은 IDE가 있습니다).
Stream.iterate (seed, seedOperator) .limit (n) .foreach (action) 은 yield 연산자와 동일하지 않지만 다음과 같은 방식으로 고유 한 생성기를 작성하는 것이 유용 할 수 있습니다.
import java.util.stream.Stream;
public class Test01 {
private static void myFoo(int someVar){
//do some work
System.out.println(someVar);
}
private static void myFoo2(){
//do some work
System.out.println("some work");
}
public static void main(String[] args) {
Stream.iterate(1, x -> x + 1).limit(15).forEach(Test01::myFoo); //var1
Stream.iterate(1, x -> x + 1).limit(10).forEach(item -> myFoo2()); //var2
}
}
또한 Observable을 "yielder"로 사용하기 위해 프로젝트에서 RXJava 를 이미 사용하고 있는지도 제안합니다 . 자신 만의 Observable을 만들면 비슷한 방식으로 사용할 수 있습니다.
public class Example extends Observable<String> {
public static void main(String[] args) {
new Example().blockingSubscribe(System.out::println); // "a", "b", "c", "d"
}
@Override
protected void subscribeActual(Observer<? super String> observer) {
observer.onNext("a"); // yield
observer.onNext("b"); // yield
observer.onNext("c"); // yield
observer.onNext("d"); // yield
observer.onComplete(); // finish
}
}
Observable은 반복자로 변환되어 더 전통적인 for 루프에서도 사용할 수 있습니다. 또한 RXJava는 정말 강력한 도구를 제공하지만 간단한 것만 필요하다면 과잉 일 수도 있습니다.
방금 여기에 다른 (MIT 라이선스) 솔루션을 게시 했습니다. 별도의 스레드에서 생산자를 시작하고 생산자와 소비자 사이에 제한된 대기열을 설정하여 생산자와 소비자 간의 버퍼링, 흐름 제어 및 병렬 파이프 라이닝을 허용하는 했습니다. 생산자가 다음 항목을 생산하는 동안 소비자는 이전 항목을 소비 할 수 있습니다.)
이 익명 내부 클래스 양식을 사용할 수 있습니다.
Iterable<T> iterable = new Producer<T>(queueSize) {
@Override
public void producer() {
produce(someT);
}
};
예를 들면 :
for (Integer item : new Producer<Integer>(/* queueSize = */ 5) {
@Override
public void producer() {
for (int i = 0; i < 20; i++) {
System.out.println("Producing " + i);
produce(i);
}
System.out.println("Producer exiting");
}
}) {
System.out.println(" Consuming " + item);
Thread.sleep(200);
}
또는 람다 표기법을 사용하여 상용구를 줄일 수 있습니다.
for (Integer item : new Producer<Integer>(/* queueSize = */ 5, producer -> {
for (int i = 0; i < 20; i++) {
System.out.println("Producing " + i);
producer.produce(i);
}
System.out.println("Producer exiting");
})) {
System.out.println(" Consuming " + item);
Thread.sleep(200);
}