blob: 58ecb3dbe63a36ee3ac5377dbcc302cb2c67098a [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package groovy.transform.stc
import org.junit.Test
import static groovy.test.GroovyAssert.assertScript
import static groovy.test.GroovyAssert.shouldFail
final class MethodReferenceTest {
private final GroovyShell shell = GroovyShell.withConfig {
imports {
star 'groovy.transform'
star 'java.util.function'
normal 'java.util.stream.Collectors'
}
}
@Test // class::instanceMethod
void testFunctionCI() {
assertScript shell, '''
@CompileStatic
void test() {
def result = [1, 2, 3].stream().map(Object::toString).collect(Collectors.toList())
assert result == ['1', '2', '3']
}
test()
'''
}
@Test // class::instanceMethod
void testFunctionCI2() {
assertScript shell, '''
@CompileStatic
void test() {
def result = [1, 2, 3].stream().map(Integer::toString).collect(Collectors.toList())
assert result == ['1', '2', '3']
}
test()
'''
}
@Test // class::instanceMethod -- GROOVY-10047
void testFunctionCI3() {
assertScript shell, '''
@CompileStatic
void test() {
List<String> list = ['a','bc','def']
Function<String,String> self = str -> str // help for toMap
def result = list.stream().collect(Collectors.toMap(self, String::length))
assert result == [a: 1, bc: 2, 'def': 3]
}
test()
'''
assertScript shell, '''
@CompileStatic
void test() {
List<String> list = ['a','bc','def']
// TODO: inference for T in toMap(Function<? super T,...>, Function<? super T,...>)
def result = list.stream().collect(Collectors.toMap(Function.<String>identity(), String::length))
assert result == [a: 1, bc: 2, 'def': 3]
}
test()
'''
}
@Test // class::instanceMethod
void testFunctionCI4() {
def err = shouldFail shell, '''
@CompileStatic
void test() {
[1, 2, 3].stream().map(String::toString).collect(Collectors.toList())
}
'''
assert err =~ /Invalid receiver type: java.lang.Integer is not compatible with java.lang.String/
}
@Test // class::instanceMethod -- GROOVY-9814
void testFunctionCI5() {
assertScript shell, '''
class One { String id }
class Two extends One { }
@CompileStatic @Immutable(knownImmutableClasses=[Function])
class FunctionHolder<T> {
Function<T, ?> extractor
def apply(T t) {
extractor.apply(t)
}
}
def fh = new FunctionHolder(One::getId)
assert fh.apply(new One(id:'abc')) == 'abc'
fh = new FunctionHolder(One::getId)
assert fh.apply(new Two(id:'xyz')) == 'xyz' // sub-type argument
'''
}
@Test // class::instanceMethod -- GROOVY-9813
void testFunctionCI6() {
String head = '''
@CompileStatic
class C {
def <T> List<T> asList(T... a) {
return Arrays.asList(a)
}
static main(args) {
'''
String tail = '''
}
}
'''
shouldFail shell, head + '''
Supplier<List> zero = C::asList
''' + tail
assertScript shell, head + '''
Function<C, List> one = C::asList
def list = one.apply(new C())
assert list.isEmpty()
''' + tail
assertScript shell, head + '''
BiFunction<C, Integer, List> two = C::asList
def list = two.apply(new C(),1)
assert list.size() == 1
assert list[0] == 1
''' + tail
}
@Test // class::instanceMethod -- GROOVY-9853
void testFunctionCI7() {
assertScript shell, '''
@CompileStatic
void test() {
ToIntFunction<CharSequence> f = CharSequence::size
int size = f.applyAsInt("")
assert size == 0
}
test()
'''
assertScript shell, '''
@CompileStatic
void test() {
ToIntFunction<CharSequence> f = CharSequence::length
int length = f.applyAsInt("")
assert length == 0
}
test()
'''
assertScript shell, '''
@CompileStatic
void test() {
Function<CharSequence,Integer> f = CharSequence::length
Integer length = f.apply("")
assert length == 0
}
test()
'''
assertScript shell, '''
import java.util.stream.IntStream
@CompileStatic
void test() {
Function<CharSequence,IntStream> f = CharSequence::chars // default method
IntStream chars = f.apply("")
assert chars.count() == 0
}
test()
'''
assertScript shell, '''
@CompileStatic
void test() {
ToIntBiFunction<CharSequence,CharSequence> f = CharSequence::compare // static method
int result = f.applyAsInt("","")
assert result == 0
}
test()
'''
}
@Test // class::instanceMethod -- GROOVY-10054, GROOVY-10734
void testFunctionCI8() {
assertScript shell, '''
class C {
String p
}
@CompileStatic
Map test(Collection<C> items) {
items.stream().collect(Collectors.groupingBy(C::getP))
}
def map = test([new C(p:'foo'), new C(p:'bar'), new C(p:'foo')])
assert map.foo.size() == 2
assert map.bar.size() == 1
'''
}
@Test // class::instanceMethod -- GROOVY-9974
void testPredicateCI() {
assertScript shell, '''
@CompileStatic
void test(List<String> strings = ['']) {
strings.removeIf(String::isEmpty)
assert strings.isEmpty()
}
test()
'''
}
@Test // class::instanceMethod -- GROOVY-11051
void testPredicateCI2() {
[['null','Empty'],['new Object()','Present']].each { value, which ->
assertScript """import java.util.concurrent.atomic.AtomicReference
def opt = new AtomicReference<Object>($value).stream()
.filter(AtomicReference::get).findFirst()
assert opt.is${which}()
"""
}
}
@Test // class::instanceMethod -- GROOVY-10791
void testBiConsumerCI() {
assertScript shell, '''
@CompileStatic
def <T> void test(List<T> list, Consumer<? super T> todo) {
BiConsumer<List<T>, Consumer<? super T>> binder = List::forEach // default method of Iterator
binder.accept(list, todo)
}
test(['works']) { assert it == 'works' }
'''
}
@Test // class::instanceMethod
void testBinaryOperatorCI() {
assertScript shell, '''
@CompileStatic
void test() {
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, BigDecimal::add)
assert result == 6.0G
}
test()
'''
}
@Test // instance::instanceMethod -- GROOVY-10933
void testConsumerII() {
assertScript shell, '''
@CompileStatic
void test() {
List<String> strings = []
Optional.of('string')
.ifPresent(strings::add)
assert strings.contains('string')
}
test()
'''
}
@Test // instance::instanceMethod -- GROOVY-11020
void testConsumerII2() {
assertScript shell, '''
def <C extends Consumer<String>> void m(C c) {
c.accept('string')
}
@CompileStatic
void test(ArrayDeque<String> strings) {
m(strings::addFirst) // NPE in STC
assert strings.contains('string')
}
test(new ArrayDeque<>())
'''
assertScript shell, '''
@Grab('org.apache.commons:commons-collections4:4.4')
import org.apache.commons.collections4.*
@CompileStatic
def test(Iterable<String> x, ArrayDeque<String> y) {
CollectionUtils.forAllButLastDo(x,y::addFirst)
IterableUtils.forEachButLast(x,y::addFirst)
Iterator<String> z = x.iterator()
IteratorUtils.forEachButLast(z,y::addFirst)
}
Iterable <String> x = ['foo','bar','baz']
ArrayDeque<String> y = []
def z = test(x,y)
assert y.join('') == 'barfoo'*3 && z == 'baz'
'''
}
@Test // instance::instanceMethod -- GROOVY-11068
void testConsumerII3() {
assertScript shell, '''
@Grab('org.apache.pdfbox:pdfbox:2.0.28')
import org.apache.pdfbox.pdmodel.*
@Grab('io.vavr:vavr:0.10.4')
import io.vavr.control.Try
@CompileStatic
def test(PDDocument doc) {
extraPages().forEach {
it.forEach(doc::addPage) // expect operand PDDocument
}
}
Try<Iterable<PDPage>> extraPages() {
Try.success([new PDPage()])
}
test(new PDDocument())
'''
}
@Test // instance::instanceMethod -- GROOVY-11068
void testConsumerII4() {
assertScript shell, '''
@Grab('org.apache.pdfbox:pdfbox:2.0.28')
import org.apache.pdfbox.pdmodel.*
@Grab('io.vavr:vavr:0.10.4')
import io.vavr.control.Try
@CompileStatic
def test(List<PDDocument> docs) {
docs.each { doc ->
extraPages().forEach {
it.forEach(doc::addPage) // expect operand PDDocument
}
}
}
Try<Iterable<PDPage>> extraPages() {
Try.success([new PDPage()])
}
test([new PDDocument()])
'''
}
@Test // instance::instanceMethod -- GROOVY-9813
void testFunctionII() {
String asList = '''
def <T> List<T> asList(T... a) {
return Arrays.asList(a)
}
'''
assertScript shell, asList + '''
@CompileStatic
void test() {
Supplier<List> zero = this::asList
def list = zero.get()
assert list.isEmpty()
}
test()
'''
assertScript shell, asList + '''
@CompileStatic
void test() {
Function<Integer, List> one = this::asList
def list = one.apply(1)
assert list.size() == 1
assert list[0] == 1
}
test()
'''
assertScript shell, asList + '''
@CompileStatic
void test() {
BiFunction<Integer, Integer, List> two = this::asList
def list = two.apply(2,3)
assert list.size() == 2
assert list[0] == 2
assert list[1] == 3
}
test()
'''
assertScript shell, asList + '''
@CompileStatic
void test() { def that = this
BiFunction<Integer, Integer, List> two = that::asList
def list = two.apply(2,3)
assert list.size() == 2
assert list[0] == 2
assert list[1] == 3
}
test()
'''
}
@Test // instance::instanceMethod -- GROOVY-10653
void testFunctionII2() {
assertScript shell, '''
class C {
List m(... args) {
[this,*args]
}
}
@CompileStatic
void test(C c) {
BiFunction<Integer, Integer, List> two = c::m
def list = two.apply(1,2)
assert list.size() == 3
assert list[0] == c
assert list[1] == 1
assert list[2] == 2
}
test(new C())
'''
}
@Test // instance::instanceGroovyMethod -- GROOVY-10653
void testFunctionII3() {
assertScript shell, '''
@CompileStatic
int test(CharSequence chars) {
IntSupplier sizing = chars::size // from StringGroovyMethods
return sizing.getAsInt()
}
int size = test("foo")
assert size == 3
'''
}
@Test // instance::instanceMethod -- GROOVY-10972
void testFunctionII4() {
assertScript shell, '''
@CompileStatic
void test() {
LinkedList<String> list = new LinkedList<>()
list.add('works')
Function<Integer,String> func = list::remove
assert func.apply(0) == 'works'
}
test()
'''
}
@Test // instance::instanceMethod -- GROOVY-10057
void testPredicateII() {
assertScript shell, '''
Class c = Integer
Predicate p
p = c::isInstance
assert p.test(null) == false
assert p.test('xx') == false
assert p.test(1234) == true
p = c.&isInstance
assert p.test(null) == false
assert p.test('xx') == false
assert p.test(1234) == true
p = o -> c.isInstance(o)
assert p.test(null) == false
assert p.test('xx') == false
assert p.test(1234) == true
p = { c.isInstance(it) }
assert p.test(null) == false
assert p.test('xx') == false
assert p.test(1234) == true
'''
}
@Test // instance::instanceMethod -- GROOVY-10994
void testPredicateII2() {
assertScript shell, '''
@CompileStatic
def <T> void test(List<T> list) {
Predicate<? super T> p = list::add
}
test([])
'''
}
@Test // instance::instanceMethod -- GROOVY-11026
void testBiFunctionII() {
assertScript shell, '''
@CompileDynamic
def <In,InOut> InOut m(BiFunction<In,InOut,InOut> beef) {
beef.apply(0,'boo')
}
@CompileStatic
String test(List<String> x) {
m(x::set) // NPE
}
String result = test(['foo','bar'])
assert result == 'foo'
'''
}
@Test // instance::instanceMethod
void testBinaryOperatorII() {
assertScript shell, '''
class Adder {
BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
}
@CompileStatic
void test() {
Adder adder = new Adder()
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
assert result == 6.0G
}
test()
'''
}
@Test // instance::instanceMethod
void testBinaryOperatorII2() {
assertScript shell, '''
class Adder {
BigDecimal add(Number a, Number b) {
((BigDecimal) a).add((BigDecimal) b)
}
}
@CompileStatic
void test() {
Adder adder = new Adder()
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
assert result == 6.0G
}
test()
'''
}
@Test // expression::instanceMethod
void testBinaryOperatorII_EXPRESSION() {
assertScript shell, '''
class Adder {
public BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
}
@CompileStatic
void test() {
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder()::add)
assert result == 6.0G
}
test()
'''
}
@Test // expression::instanceMethod
void testBinaryOperatorII_EXPRESSION2() {
assertScript shell, '''
class Adder {
BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
Adder getThis() {
return this
}
}
@CompileStatic
void test() {
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder().getThis()::add)
assert result == new BigDecimal(6)
}
test()
'''
}
@Test // instance::instanceMethod
void testBinaryOperatorII_RHS() {
assertScript shell, '''
class Adder {
BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
}
@CompileStatic
void test() {
Adder adder = new Adder()
BinaryOperator<BigDecimal> b = adder::add
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, b)
assert result == 6.0G
}
test()
'''
}
@Test // expression::instanceMethod
void testBinaryOperatorII_RHS2() {
assertScript shell, '''
class Adder {
BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
}
@CompileStatic
void test() {
BinaryOperator<BigDecimal> b = new Adder()::add
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, b)
assert result == 6.0G
}
test()
'''
}
@Test // instance::staticMethod
void testBinaryOperatorIS() {
assertScript shell, '''
class Adder {
static BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
}
@CompileStatic
void test() {
Adder adder = new Adder()
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, adder::add)
assert result == 6.0G
}
test()
'''
}
@Test // expression::staticMethod
void testBinaryOperatorIS_EXPRESSION() {
assertScript shell, '''
class Adder {
static BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
}
@CompileStatic
void test() {
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, new Adder()::add)
assert result == 6.0G
}
test()
'''
}
@Test // expression::staticMethod
void testBinaryOperatorIS_EXPRESSION2() {
assertScript shell, '''
class Adder {
static BigDecimal add(BigDecimal a, BigDecimal b) {
a.add(b)
}
static Adder newInstance() {
new Adder()
}
}
@CompileStatic
void test() {
def result = [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, Adder.newInstance()::add)
assert result == 6.0G
}
test()
'''
}
@Test // class::new
void testFunctionCN() {
assertScript shell, '''
@CompileStatic
void test() {
def result = ["1", "2", "3"].stream().map(Integer::new).collect(Collectors.toList())
assert result == [1, 2, 3]
}
test()
'''
}
@Test // class::new
void testFunctionCN2() {
assertScript shell, '''
@CompileStatic
void test() {
Function<String, Integer> f = Integer::new // deprcated; parseInt/valueOf
def result = ["1", "2", "3"].stream().map(f).collect(Collectors.toList())
assert result == [1, 2, 3]
}
test()
'''
}
@Test // class::new -- GROOVY-10033
void testFunctionCN3() {
assertScript shell, '''
@CompileStatic
class C {
C(Function<String,Integer> f) {
def i = f.apply('42')
assert i == 42
}
static test() {
new C(Integer::new)
}
}
C.test()
'''
}
@Test // class::new -- GROOVY-10033
void testFunctionCN4() {
assertScript shell, '''
class A {
A(Function<A,B> f) {
B b = f.apply(this)
assert b instanceof X.Y
}
}
class B {
B(A a) {
assert a != null
}
}
@CompileStatic
class X extends A {
public X() {
super(Y::new)
}
private static class Y extends B {
Y(A a) {
super(a)
}
}
}
new X()
'''
}
@Test // class::new -- GROOVY-10930
void testFunctionCN5() {
def err = shouldFail shell, '''
class Foo { Foo() { } }
def <T> void m(Function<String,T> fn) { }
@CompileStatic
void test() {
m(Foo::new) // ctor does not accept String
}
test()
'''
assert err =~ /Cannot find matching constructor Foo\(java.lang.String\)/
}
@Test // class::new -- GROOVY-10971
void testFunctionCN6() {
assertScript shell, '''
class Foo { Foo(String s) { } }
@CompileStatic
void test() {
Collectors.groupingBy(Foo::new) // Cannot find matching constructor Foo(Object)
}
test()
'''
}
@Test // class::new -- GROOVY-11001
void testFunctionCN7() {
assertScript shell, '''
@Grab('io.vavr:vavr:0.10.4')
import io.vavr.control.Try
class StringInputStream {
StringInputStream(String s) {
}
}
@CompileStatic
void test() {
Try.success('string').flatMap { // <U> Try<U> flatMap(Function<? super T,? extends Try<? extends U>> mapper)
Try.success(it).map(StringInputStream::new)
}
}
test()
'''
}
@Test // arrayClass::new
void testIntFunctionCN() {
assertScript shell, '''
@CompileStatic
void test() {
IntFunction<Integer[]> f = Integer[]::new;
def result = [1, 2, 3].stream().toArray(f)
assert result == new Integer[] {1, 2, 3}
}
test()
'''
}
@Test // class::staticMethod
void testFunctionCS() {
assertScript shell, '''
@CompileStatic
void test() {
def result = [1, -2, 3].stream().map(Math::abs).collect(Collectors.toList())
assert result == [1, 2, 3]
}
test()
'''
}
@Test // class::staticMethod
void testFunctionCS2() {
for (makeList in ['Collections::singletonList','List::of']) {
assertScript shell, """
@CompileStatic
def test(List<String> list) {
list.stream().collect(Collectors.toMap(Function.identity(), $makeList))
}
assert test(['x','y','z']) == [x: ['x'], y: ['y'], z: ['z']]
"""
}
}
@Test // class::staticMethod -- GROOVY-9799
void testFunctionCS3() {
assertScript shell, '''
class C {
String x
}
class D {
String x
static D from(C c) {
new D(x: c.x)
}
}
@CompileStatic
def test(C c) {
Optional.of(c).map(D::from).get()
}
def d = test(new C(x: 'x'))
assert d.x == 'x'
'''
}
@Test // class::staticMethod
void testFunctionCS4() {
assertScript shell, '''
@CompileStatic
void test() {
Function<Integer, Integer> f = Math::abs
def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
assert [1, 2, 3] == result
}
test()
'''
}
@Test // class::staticMethod
void testFunctionCS5() {
assertScript shell, '''
@CompileStatic
void test() {
def f = Math::abs // No explicit type defined, so it is actually a method closure
def result = [1, -2, 3].stream().map(f).collect(Collectors.toList())
assert [1, 2, 3] == result
}
test()
'''
}
@Test // class::staticMethod -- GROOVY-9813
void testFunctionCS6() {
assertScript shell, '''
@CompileStatic
void test() {
Supplier<List> zero = Arrays::asList
def list = zero.get()
assert list.isEmpty()
}
test()
'''
assertScript shell, '''
@CompileStatic
void test() {
Function<Integer, List> one = Arrays::asList
def list = one.apply(1)
assert list.size() == 1
assert list[0] == 1
}
test()
'''
assertScript shell, '''
@CompileStatic
void test() {
BiFunction<Integer, Integer, List> two = Arrays::asList
def list = two.apply(2,3)
assert list.size() == 2
assert list[0] == 2
assert list[1] == 3
}
test()
'''
}
@Test // class::staticMethod -- GROOVY-10807
void testFunctionCS7() {
assertScript shell, '''
@CompileStatic
class C {
public static Comparator<String> c = Comparator.<String,String>comparing(C::m)
static String m(String string) {
return string
}
}
List<String> list = ['foo','bar','baz']
list.sort(C.c)
assert list == ['bar','baz','foo']
'''
}
@Test // class::staticMethod -- GROOVY-10807
void testFunctionCS8() {
assertScript shell, '''
@CompileStatic
class C {
public static Comparator<String> c = Comparator.comparing(C::m)
static String m(String string) {
return string
}
}
List<String> list = ['foo','bar','baz']
list.sort(C.c)
assert list == ['bar','baz','foo']
'''
}
@Test // class::staticMethod -- GROOVY-11009
void testFunctionCS9() {
assertScript shell, '''
class C {
static <T> T clone(T t) { t }
// see also Object#clone()
}
@CompileStatic
Double test() {
Function<Double,Double> fn = C::clone
fn.apply(1.234d)
}
def n = test()
assert n == 1.234
'''
}
@Test // class::instanceGroovyMethod
void testFunctionCI_DGM() {
assertScript shell, '''
@CompileStatic
void test() {
def result = ['a', 'ab', 'abc'].stream().map(String::size).collect(Collectors.toList())
assert result == [1, 2, 3]
}
test()
'''
}
@Test // class::staticGroovyMethod
void testFunctionCS_DGSM() {
assertScript shell, '''
@CompileStatic
void test() {
def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
assert result.every { it instanceof Thread }
}
test()
'''
}
@Test // class::instanceGroovyMethod
void testFunctionCI_SHADOW_DGM() {
assertScript shell, '''
@CompileStatic
void test() {
def result = [[a:1], [b:2], [c:3]].stream().map(Object::toString).collect(Collectors.toList())
assert result == ['[a:1]', '[b:2]', '[c:3]']
}
test()
'''
}
@Test // class::staticGroovyMethod
void testFunctionCS_MULTI_DGSM() {
assertScript shell, '''
@CompileStatic
void test() {
def result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
assert result.every { it instanceof Thread }
result = [{}, {}, {}].stream().map(Thread::startDaemon).collect(Collectors.toList())
assert result.every { it instanceof Thread }
}
test()
'''
}
@Test
void testMethodNotFound1() {
def err = shouldFail shell, '''
@CompileStatic
void test() {
[1.0G, 2.0G, 3.0G].stream().reduce(0.0G, BigDecimal::addx)
}
'''
assert err.message.contains("Failed to find class method 'addx(java.math.BigDecimal,java.math.BigDecimal)' or instance method 'addx(java.math.BigDecimal)' for the type: java.math.BigDecimal")
}
@Test // GROOVY-9463
void testMethodNotFound2() {
def err = shouldFail shell, '''
@CompileStatic
void test() {
Function<String,String> reference = String::toLowerCaseX
}
'''
assert err.message.contains("Failed to find class method 'toLowerCaseX(java.lang.String)' or instance method 'toLowerCaseX()' for the type: java.lang.String")
}
@Test // GROOVY-10813, GROOVY-10858
void testMethodSelection() {
for (spec in ['', '<?>', '<Object>', '<? extends Object>', '<? super String>']) {
assertScript shell, """
@CompileStatic
void test() {
Consumer$spec c = this::print // overloads in Script and DefaultGroovyMethods
c.accept('hello world!')
}
test()
"""
}
for (spec in ['', '<?,?>', '<?,Object>', '<?,? extends Object>', '<?,? super String>']) {
assertScript shell, """
@CompileStatic
void test() {
BiConsumer$spec c = Object::print
c.accept(this, 'hello world!')
}
test()
"""
}
assertScript shell, '''
@CompileStatic
void test() {
BiConsumer<Script,?> c = Script::print
c.accept(this, 'hello world!')
}
test()
'''
assertScript shell, '''
@CompileStatic
void test() {
Supplier<String> s = 'x'::toString
def result = s.get()
assert result == 'x'
}
test()
'''
def err = shouldFail shell, '''
@CompileStatic
void test() {
Supplier<String> s = Object::toString // all options require an object
}
'''
assert err.message.contains("Failed to find class method 'toString()' for the type: java.lang.Object")
}
@Test // GROOVY-10859
void testDynamicMethodSelection() {
for (tag in ['@TypeChecked', '@CompileStatic', '@CompileDynamic']) {
assertScript shell, """
$tag
void test() {
def result = [[]].stream().flatMap(List::stream).toList()
assert result.isEmpty()
}
test()
"""
}
}
@Test // GROOVY-10904
void testPropertyMethodLocation() {
for (tag in ['@TypeChecked', '@CompileStatic', '@CompileDynamic']) {
assertScript shell, """
$tag
class Test {
static class Profile {
String foo, bar
}
Map<String, Profile> profiles = [new Profile()].stream()
.collect(Collectors.toMap(Profile::getFoo, Function.identity()))
static main(args) {
assert this.newInstance().getProfiles().size() == 1
}
}
"""
}
}
@Test // GROOVY-10742, GROOVY-10858
void testIncompatibleReturnType() {
def err = shouldFail shell, '''
void foo(bar) {
}
@CompileStatic
void test() {
Function<Object,String> f = this::foo
}
'''
assert err =~ /Invalid return type: void is not convertible to java.lang.String/
err = shouldFail shell, '''
@CompileStatic
void test() {
Function<Object,Number> f = Object::toString
}
'''
assert err =~ /Invalid return type: java.lang.String is not convertible to java.lang.Number/
err = shouldFail shell, '''
def m(Function<Object,Number> f) {
}
@CompileStatic
void test() {
m(Object::toString)
}
'''
assert err =~ /Invalid return type: java.lang.String is not convertible to java.lang.Number/
}
@Test // GROOVY-10269
void testNotFunctionalInterface() {
def err = shouldFail shell, '''
void foo(Integer y) {
}
void bar(Consumer<Integer> x) {
}
@CompileStatic
void test() {
bar(this::foo)
def baz = { Consumer<Integer> x -> }
baz(this::foo) // not yet supported!
}
'''
assert err =~ /The argument is a method reference, but the parameter type is not a functional interface/
}
@Test // GROOVY-10336
void testNotFunctionalInterface2() {
def err = shouldFail shell, '''
class C {
Integer m() { 1 }
}
@CompileStatic
void test() {
Supplier<Long> outer = () -> {
Closure<Long> inner = (Object o, Supplier<Integer> s) -> 2L
inner(new Object(), new C()::m) // TODO: resolve call(Object,Supplier<Integer>)
}
}
'''
assert err =~ /The argument is a method reference, but the parameter type is not a functional interface/
}
@Test // GROOVY-10635
void testRecordComponentMethodReference() {
assertScript shell, '''
record Bar(String name) {
}
def bars = [new Bar(name: 'A'), new Bar(name: 'B')]
assert bars.stream().map(Bar::name).map(String::toLowerCase).toList() == ['a', 'b']
'''
}
}