Почему не вызываются методы объекта из arraylist с помощью stream (кроме toString)?
Целый день пытаюсь разобраться с java stream, но не могу понять одной вещи.
Есть класс SomeClass, в нем есть переопределенный public String toString() и есть другие методы, например, public void someMethod().
Далее, есть ArrayList экземпляров этого класса и Stream:
List<SomeClass> someList = new ArrayList<>();
Stream someStream = someList.stream();
Если я пытаюсь с помощью stream.filter вызвать toString(), он вызывается, а если пытаюсь вызвать любой другой метод, то он не доступен.
Так как же вызывать другие методы?!
Вот мой класс SomeClass:
public class SomeClass {
String name;
SomeClass(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return name;
}
public void someMethod(){
System.out.println("someMethod()" + name);
}
}
Вот класс с методом main():
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class Test {
public static void main(String[] args) {
List<SomeClass> someList = new ArrayList<>();
someList.add(new SomeClass("A"));
someList.add(new SomeClass("B"));
Stream someStream = someList.stream();
someStream.filter(x -> "A".equals(x.toString())).forEach(x -> System.out.println(x.toString())); //работает
someStream.filter(x -> "A".equals(x.toString())).forEach(x -> x.someMethod()); //someMethod() не доступен
someStream.filter(x -> "A".equals(x.getName())).forEach(x -> x.someMethod()); //getName() тоже не доступен
}
}
Ошибка:
Cannot resolve method 'someMethod' in 'Object'
Ответы (1 шт):
Интерфейс Stream<T>, так же как и интерфейсы коллекций Collection<T>, List<T>, Set<T> являются обобщёнными, а в объявлении:
Stream someStream = someList.stream();
для экземпляра потока someStream не указан тип объектов, то есть он объявлен как "сырой" (компилятор должен был показать соответствующее предупреждение), а значит, при обработке элементов в этом потоке они обрабатываются как простые объекты, у которых существуют только методы toString, hashCode, equals и т.п.
Для решения этой проблемы достаточно правильно объявить поток:
Stream<SomeClass> someStream = someList.stream();
Но даже при корректном объявлении повторное использование потока указанным способом не будет работать, так как после первого же использования (вызова терминального метода типа forEach) поток будет закрыт, и соответственно будет выброшено исключение java.lang.IllegalStateException: stream has already been operated upon or closed.
Если нужно использовать один поток несколько раз, понадобится определить его Supplier, и вызывать поток при помощи Supplier::get:
import java.util.function.Supplier;
// ...
List<SomeClass> someList = new ArrayList<>();
someList.add(new SomeClass("A"));
someList.add(new SomeClass("B"));
Supplier<Stream<SomeClass>> someStream = () -> someList.stream();
someStream.get().filter(x -> "A".equals(x.toString())).forEach(x -> System.out.println(x.toString())); //работает
someStream.get().filter(x -> "A".equals(x.toString())).forEach(SomeClass::someMethod);
someStream.get().map(SomeClass::getName).forEach(System.out::println);
Или же можно просто каждый раз вызывать метод stream у списка someList, соответственно, проблем с типизацией также не будет:
List<SomeClass> list = Arrays.asList(new SomeClass("A"), new SomeClass("B"));
list.stream().filter(x -> "A".equals(x.toString())).forEach(System.out::println);
List<String> names = list.stream().map(SomeClass::getName).collect(Collectors.toList());