Java 8에서 스트림을 캐스트 할 수 있습니까?


160

Java 8에서 스트림을 캐스트 할 수 있습니까? 객체 목록이 있다고 가정하면 추가 객체를 모두 필터링하기 위해 이와 같은 작업을 수행 할 수 있습니다.

Stream.of(objects).filter(c -> c instanceof Client)

이 후, 고객과 무언가를하고 싶다면 각각을 캐스팅해야합니다.

Stream.of(objects).filter(c -> c instanceof Client)
    .map(c -> ((Client) c).getID()).forEach(System.out::println);

조금 추한 것 같습니다. 전체 스트림을 다른 유형으로 캐스트 할 수 있습니까? 마찬가지로 캐스팅 Stream<Object>A를 Stream<Client>?

이와 같은 작업을 수행하면 디자인이 잘못 될 수 있다는 사실을 무시하십시오. 우리는 내 컴퓨터 과학 수업에서 이와 같은 작업을 수행하므로 Java 8의 새로운 기능을 살펴 보았으며 이것이 가능한지 궁금했습니다.


3
Java 런타임의 관점에서 두 스트림 유형은 이미 동일하므로 캐스트가 필요하지 않습니다. 요령은 컴파일러를 지나서 몰래 들어가는 것입니다. (그렇게하는 것이 합리적이라고 가정합니다.)
Hot Licks

답변:


283

나는 그것을 즉시 할 수있는 방법이 없다고 생각합니다. 더 깨끗한 해결책은 다음과 같습니다.

Stream.of(objects)
    .filter(c -> c instanceof Client)
    .map(c -> (Client) c)
    .map(Client::getID)
    .forEach(System.out::println);

또는 의견에서 제안한 것처럼 cast방법을 사용할 수 있습니다. 전자는 읽기가 더 쉬울 수 있습니다.

Stream.of(objects)
    .filter(Client.class::isInstance)
    .map(Client.class::cast)
    .map(Client::getID)
    .forEach(System.out::println);

이것은 내가 찾던 것입니다. 클라이언트에 캐스팅하면을 map반환 한다고 간과 했습니다 Stream<Client>. 감사!
피션

신세대 유형의 스파게티 코드 (수직이 아닌 수평)에 빠질 위험이 있지만 흥미로운 새로운 방법 +1
robermann

@LordOfThePigs 그렇습니다. 코드가 명확 해지면 확실하지 않습니다. 나는 그 아이디어를 내 대답에 추가했다.
assylias

38
다음을 사용하여 instanceOf 필터를 "단순화"할 수 있습니다.Stream.of(objects).filter(Client.class::isInstance).[...]
Nicolas Labrot

화살이없는 부분은 정말 아름답습니다 <3
Fabich

14

ggovan 's answer 라인을 따라 다음과 같이하십시오.

/**
 * Provides various high-order functions.
 */
public final class F {
    /**
     * When the returned {@code Function} is passed as an argument to
     * {@link Stream#flatMap}, the result is a stream of instances of
     * {@code cls}.
     */
    public static <E> Function<Object, Stream<E>> instancesOf(Class<E> cls) {
        return o -> cls.isInstance(o)
                ? Stream.of(cls.cast(o))
                : Stream.empty();
    }
}

이 헬퍼 기능 사용 :

Stream.of(objects).flatMap(F.instancesOf(Client.class))
        .map(Client::getId)
        .forEach(System.out::println);

10

파티에 늦었지만 유용한 답변이라고 생각합니다.

flatMap 가장 짧은 방법입니다.

Stream.of(objects).flatMap(o->(o instanceof Client)?Stream.of((Client)o):Stream.empty())

경우 oA는 Client다음 그렇지 않으면 빈 스트림을 사용, 하나의 요소 스트림을 생성합니다. 그런 다음이 개울은로 편평 해집니다 Stream<Client>.


나는 이것을 구현하려고했지만 내 클래스가 "체크되지 않거나 안전하지 않은 작업을 사용한다"라는 경고를 받았다.
aweibell

불행히도, 그렇습니다. 연산자 가 if/else아닌 ?:연산자 를 사용하면 경고가 표시되지 않습니다. 경고를 안전하게 억제 할 수 있으므로 안심하십시오.
ggovan

3
사실이 이상하다 Stream.of(objects).filter(o->o instanceof Client).map(o -> (Client)o)거나 Stream.of(objects).filter(Client.class::isInstance).map(Client.class::cast).
Didier L

4

조금 추한 것 같습니다. 전체 스트림을 다른 유형으로 캐스트 할 수 있습니까? 마찬가지로 캐스팅 Stream<Object>A를 Stream<Client>?

불가능합니다. 이것은 Java 8의 새로운 기능이 아닙니다. 이것은 제네릭에만 해당됩니다. A는 List<Object>의 슈퍼 유형이 아닌 List<String>당신이 단지를 캐스팅 할 수 있도록 List<Object>A를List<String> .

여기서도 비슷한 문제입니다. 당신은 캐스트 수 없습니다 Stream<Object>Stream<Client>. 물론 다음과 같이 간접적으로 캐스팅 할 수 있습니다.

Stream<Client> intStream = (Stream<Client>) (Stream<?>)stream;

그러나 그것은 안전하지 않으며 런타임에 실패 할 수 있습니다. 그 이유는 Java의 제네릭이 삭제를 사용하여 구현되기 때문입니다. 따라서 어떤 유형의 Stream런타임에 사용 가능한 유형 정보가 없습니다 . 모든 것이 단지Stream 입니다.

BTW, 당신의 접근 방식에 어떤 문제가 있습니까? 나에게 잘 보인다.


2
@DR Generics C#는 reification을 사용하여 구현되는 반면 Java에서는 erasure를 사용하여 구현됩니다. 둘 다 다른 방식으로 기본적으로 구현됩니다. 따라서 두 언어 모두에서 동일한 방식으로 작동 할 것으로 기대할 수 없습니다.
Rohit Jain

1
@DR 저는 초보자가 Java의 제네릭 개념을 이해하는 데 많은 문제가 있음을 이해합니다. 그리고 C #을 사용하지 않기 때문에 비교에 대해 자세히 설명 할 수 없습니다. 그러나 IMO를 이런 방식으로 구현 한 동기는 JVM 구현에서 큰 변화를 피하는 것이 었습니다.
Rohit Jain

1
왜 "런타임에 확실히 실패"합니까? 말했듯이 (일반적인) 유형 정보가 없으므로 런타임에서 확인할 것이 없습니다. 잘못된 유형이 전달되면 런타임에 실패 할 있지만 그에 대한 "확실성"은 없습니다.
핫 릭

1
@RohitJain : Java의 일반적인 개념을 비판하지는 않지만이 단일 결과로 여전히 추악한 코드가 생성됩니다. ;-)
DR

1
@DR-Java 제네릭은 git-go에서 추악합니다. 대부분 C ++ 기능은 정욕입니다.
핫 릭
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.