Google Guava與函數式編程相關的類

Functional idioms:Guava的函數習語可以大大簡化代碼。

1.注意事項

Java 8包括java.util.functionjava.util.stream軟件包,它們取代了Guava函數編程類,用於該語言級別的項目。

雖然Guava的函數工具類可在Java 8之前的Java版本上使用,但沒有Java 8的函數式編程需要笨拙且冗長地使用匿名類。

過度使用Guava的函數式編程習語用法會導致冗長、混亂、不可讀和低效的代碼。這些是迄今爲止最容易(也是最常見)的Guava濫用部分,當你費力地使代碼變成"一行"時,Guava團隊就會哭泣。

比較以下代碼:

Function<String, Integer> lengthFunction = new Function<String, Integer>() {
  public Integer apply(String string) {
    return string.length();
  }
};
Predicate<String> allCaps = new Predicate<String>() {
  public boolean apply(String string) {
    return CharMatcher.javaUpperCase().matchesAllOf(string);
  }
};
Multiset<Integer> lengths = HashMultiset.create(
  Iterables.transform(Iterables.filter(strings, allCaps), lengthFunction));

或流利的版本

Multiset<Integer> lengths = HashMultiset.create(
  FluentIterable.from(strings)
    .filter(new Predicate<String>() {
       public boolean apply(String string) {
         return CharMatcher.javaUpperCase().matchesAllOf(string);
       }
     })
    .transform(new Function<String, Integer>() {
       public Integer apply(String string) {
         return string.length();
       }
     }));

同:

Multiset<Integer> lengths = HashMultiset.create();
for (String string : strings) {
  if (CharMatcher.javaUpperCase().matchesAllOf(string)) {
    lengths.add(string.length());
  }
}

即使使用靜態導入,甚至將FunctionPredicate聲明移至其它文件,第一個實現也不太簡潔,可讀性和效率較低。

命令式代碼應該是默認選擇,也是Java 7的第一選擇。除非絕對確定以下其中一項,否則不應使用函數式用法:

  • 使用函數式用法將節省整個項目的代碼行。在上面的示例中,"函數式"版本使用11行,而命令式版本使用6行。將函數的定義移至另一個文件或常量中並沒有幫助。
  • 爲了提高效率,你需要對轉換後的集合進行延遲計算的視圖,並且不能滿足顯式計算的集合。另外,你已經閱讀並重新閱讀了Effective Java,第55項,除了遵循這些說明之外,你實際上已經完成了基準測試以證明該版本更快,並且可以引用數字進行證明。

請確保在使用Guava的函數工具類時,傳統的命令式處理方式並沒有更好的可讀性。試着寫出來。真的那麼糟糕嗎?這比你將要嘗試的笨拙的函數式方法更具可讀性嗎?

2.函數和謂語

本文僅討論那些直接處理FunctionPredicate的Guava功能。其它一些工具與“函數式”相關聯,例如級聯和可在恆定時間內返回視圖的其它方法。嘗試查看集合工具文章。

Guava提供了兩個基本的"函數式"接口:

  • Function<A, B>具有單個方法B apply(A input)。通常希望Function的實例具有參照透明性——無副作用——並且與equals一致,即a.equals(b)表示function.apply(a).equals(function.apply(b))
  • Predicate<T>具有單個方法boolean apply(T input)。通常希望Predicate實例是無副作用的並且與equals一致。

3.特殊謂語

字符有自己的Predicate的專用版本CharMatcher,它通常對於這些需求而言更加高效和有用。CharMatcher已經實現了Predicate<Character>,可以相應地使用,而從PredicateCharMatcher的轉換可以使用CharMatcher.forPredicate完成。有關詳細信息,請參閱CharMatcher文章

此外,對於可比較的類型和基於比較的謂語,可以使用Range類型實現大多數需求,它實現了不可變的範圍。Range類型實現了Predicate,測試是否包含在範圍內。例如,Range.atMost(2)是一個完全有效的Predicate<Integer>。有關使用範圍的更多詳細信息參見[Range文章]。

4.操作函數和謂語

Functions中提供了簡單的Function構造和操作方法,包括:

有關詳細信息,請查閱Javadoc。

Predicates謂語中有很多可用的構造和操作方法,示例包括:

有關詳細信息,請查閱Javadoc。

5.使用

Guava提供了許多使用函數和謂語來操作集合的工具。這些通常可以在集合工具類IterablesListsSetsMapsMultimaps等中找到。

6.謂語

謂語最基本的用途是過濾集合。所有Guava過濾器方法都返回視圖。

集合類型 過濾方法
Iterable Iterables.filter(Iterable, Predicate) FluentIterable.filter(Predicate)

7.使用示例

7.1Function

import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.*;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.google.common.testing.ClassSanityTester;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.SerializableTester;
import junit.framework.TestCase;

import java.io.Serializable;
import java.util.Map;

public class FunctionsTest extends TestCase {

    public void testIdentity_same() {
        Function<String, String> identity = Functions.identity();
        assertNull(identity.apply(null));
        assertSame("foo", identity.apply("foo"));
    }

    public void testIdentity_notSame() {
        Function<Long, Long> identity = Functions.identity();
        assertNotSame(new Long(135135L), identity.apply(new Long(135135L)));
    }

    @GwtIncompatible // SerializableTester
    public void testIdentitySerializable() {
        checkCanReserializeSingleton(Functions.identity());
    }

    public void testToStringFunction_apply() {
        assertEquals("3", Functions.toStringFunction().apply(3));
        assertEquals("hiya", Functions.toStringFunction().apply("hiya"));
        assertEquals(
                "I'm a string",
                Functions.toStringFunction()
                        .apply(
                                new Object() {
                                    @Override
                                    public String toString() {
                                        return "I'm a string";
                                    }
                                }));
        try {
            Functions.toStringFunction().apply(null);
            fail("expected NullPointerException");
        } catch (NullPointerException expected) {
        }
    }

    @GwtIncompatible // SerializableTester
    public void testToStringFunctionSerializable() {
        checkCanReserializeSingleton(Functions.toStringFunction());
    }

    @GwtIncompatible // NullPointerTester
    public void testNullPointerExceptions() {
        NullPointerTester tester = new NullPointerTester();
        tester.testAllPublicStaticMethods(Functions.class);
    }

    public void testForMapWithoutDefault() {
        Map<String, Integer> map = Maps.newHashMap();
        map.put("One", 1);
        map.put("Three", 3);
        map.put("Null", null);
        Function<String, Integer> function = Functions.forMap(map);

        assertEquals(1, function.apply("One").intValue());
        assertEquals(3, function.apply("Three").intValue());
        assertNull(function.apply("Null"));

        try {
            function.apply("Two");
            fail();
        } catch (IllegalArgumentException expected) {
        }

        new EqualsTester()
                .addEqualityGroup(function, Functions.forMap(map))
                .addEqualityGroup(Functions.forMap(map, 42))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testForMapWithoutDefaultSerializable() {
        checkCanReserialize(Functions.forMap(ImmutableMap.of(1, 2)));
    }

    public void testForMapWithDefault() {
        Map<String, Integer> map = Maps.newHashMap();
        map.put("One", 1);
        map.put("Three", 3);
        map.put("Null", null);
        Function<String, Integer> function = Functions.forMap(map, 42);

        assertEquals(1, function.apply("One").intValue());
        assertEquals(42, function.apply("Two").intValue());
        assertEquals(3, function.apply("Three").intValue());
        assertNull(function.apply("Null"));

        new EqualsTester()
                .addEqualityGroup(function, Functions.forMap(map, 42))
                .addEqualityGroup(Functions.forMap(map))
                .addEqualityGroup(Functions.forMap(map, null))
                .addEqualityGroup(Functions.forMap(map, 43))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testForMapWithDefault_includeSerializable() {
        Map<String, Integer> map = Maps.newHashMap();
        map.put("One", 1);
        map.put("Three", 3);
        Function<String, Integer> function = Functions.forMap(map, 42);

        assertEquals(1, function.apply("One").intValue());
        assertEquals(42, function.apply("Two").intValue());
        assertEquals(3, function.apply("Three").intValue());

        new EqualsTester()
                .addEqualityGroup(
                        function, Functions.forMap(map, 42), SerializableTester.reserialize(function))
                .addEqualityGroup(Functions.forMap(map))
                .addEqualityGroup(Functions.forMap(map, null))
                .addEqualityGroup(Functions.forMap(map, 43))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testForMapWithDefaultSerializable() {
        checkCanReserialize(Functions.forMap(ImmutableMap.of(1, 2), 3));
    }

    public void testForMapWithDefault_null() {
        ImmutableMap<String, Integer> map = ImmutableMap.of("One", 1);
        Function<String, Integer> function = Functions.forMap(map, null);

        assertEquals((Integer) 1, function.apply("One"));
        assertNull(function.apply("Two"));

        // check basic sanity of equals and hashCode
        new EqualsTester()
                .addEqualityGroup(function)
                .addEqualityGroup(Functions.forMap(map, 1))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testForMapWithDefault_null_compareWithSerializable() {
        ImmutableMap<String, Integer> map = ImmutableMap.of("One", 1);
        Function<String, Integer> function = Functions.forMap(map, null);

        assertEquals((Integer) 1, function.apply("One"));
        assertNull(function.apply("Two"));

        // check basic sanity of equals and hashCode
        new EqualsTester()
                .addEqualityGroup(function, SerializableTester.reserialize(function))
                .addEqualityGroup(Functions.forMap(map, 1))
                .testEquals();
    }

    public void testForMapWildCardWithDefault() {
        Map<String, Integer> map = Maps.newHashMap();
        map.put("One", 1);
        map.put("Three", 3);
        Number number = Double.valueOf(42);
        Function<String, Number> function = Functions.forMap(map, number);

        assertEquals(1, function.apply("One").intValue());
        assertEquals(number, function.apply("Two"));
        assertEquals(3L, function.apply("Three").longValue());
    }

    public void testComposition() {
        Map<String, Integer> mJapaneseToInteger = Maps.newHashMap();
        mJapaneseToInteger.put("Ichi", 1);
        mJapaneseToInteger.put("Ni", 2);
        mJapaneseToInteger.put("San", 3);
        Function<String, Integer> japaneseToInteger = Functions.forMap(mJapaneseToInteger);

        Map<Integer, String> mIntegerToSpanish = Maps.newHashMap();
        mIntegerToSpanish.put(1, "Uno");
        mIntegerToSpanish.put(3, "Tres");
        mIntegerToSpanish.put(4, "Cuatro");
        Function<Integer, String> integerToSpanish = Functions.forMap(mIntegerToSpanish);

        Function<String, String> japaneseToSpanish =
                Functions.compose(integerToSpanish, japaneseToInteger);

        assertEquals("Uno", japaneseToSpanish.apply("Ichi"));
        try {
            japaneseToSpanish.apply("Ni");
            fail();
        } catch (IllegalArgumentException e) {
        }
        assertEquals("Tres", japaneseToSpanish.apply("San"));
        try {
            japaneseToSpanish.apply("Shi");
            fail();
        } catch (IllegalArgumentException e) {
        }

        new EqualsTester()
                .addEqualityGroup(japaneseToSpanish, Functions.compose(integerToSpanish, japaneseToInteger))
                .addEqualityGroup(japaneseToInteger)
                .addEqualityGroup(integerToSpanish)
                .addEqualityGroup(Functions.compose(japaneseToInteger, integerToSpanish))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testComposition_includeReserializabled() {
        Map<String, Integer> mJapaneseToInteger = Maps.newHashMap();
        mJapaneseToInteger.put("Ichi", 1);
        mJapaneseToInteger.put("Ni", 2);
        mJapaneseToInteger.put("San", 3);
        Function<String, Integer> japaneseToInteger = Functions.forMap(mJapaneseToInteger);

        Map<Integer, String> mIntegerToSpanish = Maps.newHashMap();
        mIntegerToSpanish.put(1, "Uno");
        mIntegerToSpanish.put(3, "Tres");
        mIntegerToSpanish.put(4, "Cuatro");
        Function<Integer, String> integerToSpanish = Functions.forMap(mIntegerToSpanish);

        Function<String, String> japaneseToSpanish =
                Functions.compose(integerToSpanish, japaneseToInteger);

        new EqualsTester()
                .addEqualityGroup(
                        japaneseToSpanish,
                        Functions.compose(integerToSpanish, japaneseToInteger),
                        SerializableTester.reserialize(japaneseToSpanish))
                .addEqualityGroup(japaneseToInteger)
                .addEqualityGroup(integerToSpanish)
                .addEqualityGroup(Functions.compose(japaneseToInteger, integerToSpanish))
                .testEquals();
    }

    public void testCompositionWildcard() {
        Map<String, Integer> mapJapaneseToInteger = Maps.newHashMap();
        Function<String, Integer> japaneseToInteger = Functions.forMap(mapJapaneseToInteger);

        Function<Object, String> numberToSpanish = Functions.constant("Yo no se");

        Function<String, String> japaneseToSpanish =
                Functions.compose(numberToSpanish, japaneseToInteger);
    }

    private static class HashCodeFunction implements Function<Object, Integer> {
        @Override
        public Integer apply(Object o) {
            return (o == null) ? 0 : o.hashCode();
        }
    }

    public void testComposeOfFunctionsIsAssociative() {
        Map<Float, String> m = ImmutableMap.of(4.0f, "A", 3.0f, "B", 2.0f, "C", 1.0f, "D");
        Function<? super Integer, Boolean> h = Functions.constant(Boolean.TRUE);
        Function<? super String, Integer> g = new HashCodeFunction();
        Function<Float, String> f = Functions.forMap(m, "F");

        Function<Float, Boolean> c1 = Functions.compose(Functions.compose(h, g), f);
        Function<Float, Boolean> c2 = Functions.compose(h, Functions.compose(g, f));

        // Might be nice (eventually) to have:
        //     assertEquals(c1, c2);

        // But for now, settle for this:
        assertEquals(c1.hashCode(), c2.hashCode());

        assertEquals(c1.apply(1.0f), c2.apply(1.0f));
        assertEquals(c1.apply(5.0f), c2.apply(5.0f));
    }

    public void testComposeOfPredicateAndFunctionIsAssociative() {
        Map<Float, String> m = ImmutableMap.of(4.0f, "A", 3.0f, "B", 2.0f, "C", 1.0f, "D");
        Predicate<? super Integer> h = Predicates.equalTo(42);
        Function<? super String, Integer> g = new HashCodeFunction();
        Function<Float, String> f = Functions.forMap(m, "F");

        Predicate<Float> p1 = Predicates.compose(Predicates.compose(h, g), f);
        Predicate<Float> p2 = Predicates.compose(h, Functions.compose(g, f));

        // Might be nice (eventually) to have:
        //     assertEquals(p1, p2);

        // But for now, settle for this:
        assertEquals(p1.hashCode(), p2.hashCode());

        assertEquals(p1.apply(1.0f), p2.apply(1.0f));
        assertEquals(p1.apply(5.0f), p2.apply(5.0f));
    }

    public void testForPredicate() {
        Function<Object, Boolean> alwaysTrue = Functions.forPredicate(Predicates.alwaysTrue());
        Function<Object, Boolean> alwaysFalse = Functions.forPredicate(Predicates.alwaysFalse());

        assertTrue(alwaysTrue.apply(0));
        assertFalse(alwaysFalse.apply(0));

        new EqualsTester()
                .addEqualityGroup(alwaysTrue, Functions.forPredicate(Predicates.alwaysTrue()))
                .addEqualityGroup(alwaysFalse)
                .addEqualityGroup(Functions.identity())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testForPredicateSerializable() {
        checkCanReserialize(Functions.forPredicate(Predicates.equalTo(5)));
    }

    public void testConstant() {
        Function<Object, Object> f = Functions.<Object>constant("correct");
        assertEquals("correct", f.apply(new Object()));
        assertEquals("correct", f.apply(null));

        Function<Object, String> g = Functions.constant(null);
        assertEquals(null, g.apply(2));
        assertEquals(null, g.apply(null));

        new EqualsTester()
                .addEqualityGroup(f, Functions.constant("correct"))
                .addEqualityGroup(Functions.constant("incorrect"))
                .addEqualityGroup(Functions.toStringFunction())
                .addEqualityGroup(g)
                .testEquals();

        new EqualsTester()
                .addEqualityGroup(g, Functions.constant(null))
                .addEqualityGroup(Functions.constant("incorrect"))
                .addEqualityGroup(Functions.toStringFunction())
                .addEqualityGroup(f)
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testConstantSerializable() {
        checkCanReserialize(Functions.constant(5));
    }

    private static class CountingSupplier implements Supplier<Integer>, Serializable {

        private static final long serialVersionUID = 0;

        private int value;

        @Override
        public Integer get() {
            return ++value;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof CountingSupplier) {
                return this.value == ((CountingSupplier) obj).value;
            }
            return false;
        }

        @Override
        public int hashCode() {
            return value;
        }
    }

    public void testForSupplier() {
        Supplier<Integer> supplier = new CountingSupplier();
        Function<Object, Integer> function = Functions.forSupplier(supplier);

        assertEquals(1, (int) function.apply(null));
        assertEquals(2, (int) function.apply("foo"));

        new EqualsTester()
                .addEqualityGroup(function, Functions.forSupplier(supplier))
                .addEqualityGroup(Functions.forSupplier(new CountingSupplier()))
                .addEqualityGroup(Functions.forSupplier(Suppliers.ofInstance(12)))
                .addEqualityGroup(Functions.toStringFunction())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testForSupplierSerializable() {
        checkCanReserialize(Functions.forSupplier(new CountingSupplier()));
    }

    @GwtIncompatible // reflection
    public void testNulls() throws Exception {
        new ClassSanityTester().forAllPublicStaticMethods(Functions.class).testNulls();
    }

    @GwtIncompatible // reflection
    // (I suspect that this and the other similar failures happen with ArbitraryInstances proxies.)
    public void testEqualsAndSerializable() throws Exception {
        new ClassSanityTester().forAllPublicStaticMethods(Functions.class).testEqualsAndSerializable();
    }

    @GwtIncompatible // SerializableTester
    private static <Y> void checkCanReserialize(Function<? super Integer, Y> f) {
        Function<? super Integer, Y> g = SerializableTester.reserializeAndAssert(f);
        for (int i = 1; i < 5; i++) {
            // convoluted way to check that the same result happens from each
            Y expected = null;
            try {
                expected = f.apply(i);
            } catch (IllegalArgumentException e) {
                try {
                    g.apply(i);
                    fail();
                } catch (IllegalArgumentException ok) {
                    continue;
                }
            }
            assertEquals(expected, g.apply(i));
        }
    }

    @GwtIncompatible // SerializableTester
    private static <Y> void checkCanReserializeSingleton(Function<? super String, Y> f) {
        Function<? super String, Y> g = SerializableTester.reserializeAndAssert(f);
        assertSame(f, g);
        for (Integer i = 1; i < 5; i++) {
            assertEquals(f.apply(i.toString()), g.apply(i.toString()));
        }
    }
}

7.2Predicate

import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.*;
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.ClassSanityTester;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.SerializableTester;
import junit.framework.AssertionFailedError;
import junit.framework.TestCase;
import java.io.Serializable;
import java.util.*;
import java.util.regex.Pattern;

import static com.google.common.base.CharMatcher.whitespace;
import static com.google.common.collect.Lists.newArrayList;

public class PredicatesTest extends TestCase {

    private static final Predicate<Integer> TRUE = Predicates.alwaysTrue();
    private static final Predicate<Integer> FALSE = Predicates.alwaysFalse();
    private static final Predicate<Integer> NEVER_REACHED =
            new Predicate<Integer>() {
                @Override
                public boolean apply(Integer i) {
                    throw new AssertionFailedError("This predicate should never have been evaluated");
                }
            };

    /**
     * Instantiable predicate with reasonable hashCode() and equals() methods.
     */
    static class IsOdd implements Predicate<Integer>, Serializable {
        private static final long serialVersionUID = 0x150ddL;

        @Override
        public boolean apply(Integer i) {
            return (i.intValue() & 1) == 1;
        }

        @Override
        public int hashCode() {
            return 0x150dd;
        }

        @Override
        public boolean equals(Object obj) {
            return obj instanceof IsOdd;
        }

        @Override
        public String toString() {
            return "IsOdd";
        }
    }

    /**
     * Generates a new Predicate per call.
     *
     * <p>Creating a new Predicate each time helps catch cases where code is using {@code x == y}
     * instead of {@code x.equals(y)}.
     */
    private static IsOdd isOdd() {
        return new IsOdd();
    }

    /*
     * Tests for Predicates.alwaysTrue().
     */

    public void testAlwaysTrue_apply() {
        assertEvalsToTrue(Predicates.alwaysTrue());
    }

    public void testAlwaysTrue_equality() throws Exception {
        new EqualsTester()
                .addEqualityGroup(TRUE, Predicates.alwaysTrue())
                .addEqualityGroup(isOdd())
                .addEqualityGroup(Predicates.alwaysFalse())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testAlwaysTrue_serialization() {
        checkSerialization(Predicates.alwaysTrue());
    }

    /*
     * Tests for Predicates.alwaysFalse().
     */

    public void testAlwaysFalse_apply() throws Exception {
        assertEvalsToFalse(Predicates.alwaysFalse());
    }

    public void testAlwaysFalse_equality() throws Exception {
        new EqualsTester()
                .addEqualityGroup(FALSE, Predicates.alwaysFalse())
                .addEqualityGroup(isOdd())
                .addEqualityGroup(Predicates.alwaysTrue())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testAlwaysFalse_serialization() {
        checkSerialization(Predicates.alwaysFalse());
    }

    /*
     * Tests for Predicates.not(predicate).
     */

    public void testNot_apply() {
        assertEvalsToTrue(Predicates.not(FALSE));
        assertEvalsToFalse(Predicates.not(TRUE));
        assertEvalsLikeOdd(Predicates.not(Predicates.not(isOdd())));
    }

    public void testNot_equality() {
        new EqualsTester()
                .addEqualityGroup(Predicates.not(isOdd()), Predicates.not(isOdd()))
                .addEqualityGroup(Predicates.not(TRUE))
                .addEqualityGroup(isOdd())
                .testEquals();
    }

    public void testNot_equalityForNotOfKnownValues() {
        new EqualsTester()
                .addEqualityGroup(TRUE, Predicates.alwaysTrue())
                .addEqualityGroup(FALSE)
                .addEqualityGroup(Predicates.not(TRUE))
                .testEquals();

        new EqualsTester()
                .addEqualityGroup(FALSE, Predicates.alwaysFalse())
                .addEqualityGroup(TRUE)
                .addEqualityGroup(Predicates.not(FALSE))
                .testEquals();

        new EqualsTester()
                .addEqualityGroup(Predicates.isNull(), Predicates.isNull())
                .addEqualityGroup(Predicates.notNull())
                .addEqualityGroup(Predicates.not(Predicates.isNull()))
                .testEquals();

        new EqualsTester()
                .addEqualityGroup(Predicates.notNull(), Predicates.notNull())
                .addEqualityGroup(Predicates.isNull())
                .addEqualityGroup(Predicates.not(Predicates.notNull()))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testNot_serialization() {
        checkSerialization(Predicates.not(isOdd()));
    }

    /*
     * Tests for all the different flavors of Predicates.and().
     */

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_applyNoArgs() {
        assertEvalsToTrue(Predicates.and());
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_equalityNoArgs() {
        new EqualsTester()
                .addEqualityGroup(Predicates.and(), Predicates.and())
                .addEqualityGroup(Predicates.and(FALSE))
                .addEqualityGroup(Predicates.or())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testAnd_serializationNoArgs() {
        checkSerialization(Predicates.and());
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_applyOneArg() {
        assertEvalsLikeOdd(Predicates.and(isOdd()));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_equalityOneArg() {
        Object[] notEqualObjects = {Predicates.and(NEVER_REACHED, FALSE)};
        new EqualsTester()
                .addEqualityGroup(Predicates.and(NEVER_REACHED), Predicates.and(NEVER_REACHED))
                .addEqualityGroup(notEqualObjects)
                .addEqualityGroup(Predicates.and(isOdd()))
                .addEqualityGroup(Predicates.and())
                .addEqualityGroup(Predicates.or(NEVER_REACHED))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testAnd_serializationOneArg() {
        checkSerialization(Predicates.and(isOdd()));
    }

    public void testAnd_applyBinary() {
        assertEvalsLikeOdd(Predicates.and(isOdd(), TRUE));
        assertEvalsLikeOdd(Predicates.and(TRUE, isOdd()));
        assertEvalsToFalse(Predicates.and(FALSE, NEVER_REACHED));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_equalityBinary() {
        new EqualsTester()
                .addEqualityGroup(Predicates.and(TRUE, NEVER_REACHED), Predicates.and(TRUE, NEVER_REACHED))
                .addEqualityGroup(Predicates.and(NEVER_REACHED, TRUE))
                .addEqualityGroup(Predicates.and(TRUE))
                .addEqualityGroup(Predicates.or(TRUE, NEVER_REACHED))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testAnd_serializationBinary() {
        checkSerialization(Predicates.and(TRUE, isOdd()));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_applyTernary() {
        assertEvalsLikeOdd(Predicates.and(isOdd(), TRUE, TRUE));
        assertEvalsLikeOdd(Predicates.and(TRUE, isOdd(), TRUE));
        assertEvalsLikeOdd(Predicates.and(TRUE, TRUE, isOdd()));
        assertEvalsToFalse(Predicates.and(TRUE, FALSE, NEVER_REACHED));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_equalityTernary() {
        new EqualsTester()
                .addEqualityGroup(
                        Predicates.and(TRUE, isOdd(), NEVER_REACHED),
                        Predicates.and(TRUE, isOdd(), NEVER_REACHED))
                .addEqualityGroup(Predicates.and(isOdd(), NEVER_REACHED, TRUE))
                .addEqualityGroup(Predicates.and(TRUE))
                .addEqualityGroup(Predicates.or(TRUE, isOdd(), NEVER_REACHED))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testAnd_serializationTernary() {
        checkSerialization(Predicates.and(TRUE, isOdd(), FALSE));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_applyIterable() {
        Collection<Predicate<Integer>> empty = Arrays.asList();
        assertEvalsToTrue(Predicates.and(empty));
        assertEvalsLikeOdd(Predicates.and(Arrays.asList(isOdd())));
        assertEvalsLikeOdd(Predicates.and(Arrays.asList(TRUE, isOdd())));
        assertEvalsToFalse(Predicates.and(Arrays.asList(FALSE, NEVER_REACHED)));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_equalityIterable() {
        new EqualsTester()
                .addEqualityGroup(
                        Predicates.and(Arrays.asList(TRUE, NEVER_REACHED)),
                        Predicates.and(Arrays.asList(TRUE, NEVER_REACHED)),
                        Predicates.and(TRUE, NEVER_REACHED))
                .addEqualityGroup(Predicates.and(FALSE, NEVER_REACHED))
                .addEqualityGroup(Predicates.or(TRUE, NEVER_REACHED))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testAnd_serializationIterable() {
        checkSerialization(Predicates.and(Arrays.asList(TRUE, FALSE)));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testAnd_arrayDefensivelyCopied() {
        Predicate[] array = {Predicates.alwaysFalse()};
        Predicate<Object> predicate = Predicates.and(array);
        assertFalse(predicate.apply(1));
        array[0] = Predicates.alwaysTrue();
        assertFalse(predicate.apply(1));
    }

    public void testAnd_listDefensivelyCopied() {
        List<Predicate<Object>> list = newArrayList();
        Predicate<Object> predicate = Predicates.and(list);
        assertTrue(predicate.apply(1));
        list.add(Predicates.alwaysFalse());
        assertTrue(predicate.apply(1));
    }

    public void testAnd_iterableDefensivelyCopied() {
        final List<Predicate<Object>> list = newArrayList();
        Iterable<Predicate<Object>> iterable =
                new Iterable<Predicate<Object>>() {
                    @Override
                    public Iterator<Predicate<Object>> iterator() {
                        return list.iterator();
                    }
                };
        Predicate<Object> predicate = Predicates.and(iterable);
        assertTrue(predicate.apply(1));
        list.add(Predicates.alwaysFalse());
        assertTrue(predicate.apply(1));
    }

    /*
     * Tests for all the different flavors of Predicates.or().
     */

    @SuppressWarnings("unchecked") // varargs
    public void testOr_applyNoArgs() {
        assertEvalsToFalse(Predicates.or());
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_equalityNoArgs() {
        new EqualsTester()
                .addEqualityGroup(Predicates.or(), Predicates.or())
                .addEqualityGroup(Predicates.or(TRUE))
                .addEqualityGroup(Predicates.and())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testOr_serializationNoArgs() {
        checkSerialization(Predicates.or());
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_applyOneArg() {
        assertEvalsToTrue(Predicates.or(TRUE));
        assertEvalsToFalse(Predicates.or(FALSE));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_equalityOneArg() {
        new EqualsTester()
                .addEqualityGroup(Predicates.or(NEVER_REACHED), Predicates.or(NEVER_REACHED))
                .addEqualityGroup(Predicates.or(NEVER_REACHED, TRUE))
                .addEqualityGroup(Predicates.or(TRUE))
                .addEqualityGroup(Predicates.or())
                .addEqualityGroup(Predicates.and(NEVER_REACHED))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testOr_serializationOneArg() {
        checkSerialization(Predicates.or(isOdd()));
    }

    public void testOr_applyBinary() {
        Predicate<Integer> falseOrFalse = Predicates.or(FALSE, FALSE);
        Predicate<Integer> falseOrTrue = Predicates.or(FALSE, TRUE);
        Predicate<Integer> trueOrAnything = Predicates.or(TRUE, NEVER_REACHED);

        assertEvalsToFalse(falseOrFalse);
        assertEvalsToTrue(falseOrTrue);
        assertEvalsToTrue(trueOrAnything);
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_equalityBinary() {
        new EqualsTester()
                .addEqualityGroup(Predicates.or(FALSE, NEVER_REACHED), Predicates.or(FALSE, NEVER_REACHED))
                .addEqualityGroup(Predicates.or(NEVER_REACHED, FALSE))
                .addEqualityGroup(Predicates.or(TRUE))
                .addEqualityGroup(Predicates.and(FALSE, NEVER_REACHED))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testOr_serializationBinary() {
        checkSerialization(Predicates.or(isOdd(), TRUE));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_applyTernary() {
        assertEvalsLikeOdd(Predicates.or(isOdd(), FALSE, FALSE));
        assertEvalsLikeOdd(Predicates.or(FALSE, isOdd(), FALSE));
        assertEvalsLikeOdd(Predicates.or(FALSE, FALSE, isOdd()));
        assertEvalsToTrue(Predicates.or(FALSE, TRUE, NEVER_REACHED));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_equalityTernary() {
        new EqualsTester()
                .addEqualityGroup(
                        Predicates.or(FALSE, NEVER_REACHED, TRUE), Predicates.or(FALSE, NEVER_REACHED, TRUE))
                .addEqualityGroup(Predicates.or(TRUE, NEVER_REACHED, FALSE))
                .addEqualityGroup(Predicates.or(TRUE))
                .addEqualityGroup(Predicates.and(FALSE, NEVER_REACHED, TRUE))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testOr_serializationTernary() {
        checkSerialization(Predicates.or(FALSE, isOdd(), TRUE));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_applyIterable() {
        Predicate<Integer> vacuouslyFalse = Predicates.or(Collections.<Predicate<Integer>>emptyList());
        Predicate<Integer> troo = Predicates.or(Collections.singletonList(TRUE));
        /*
         * newLinkedList() takes varargs. TRUE and FALSE are both instances of
         * Predicate<Integer>, so the call is safe.
         */
        Predicate<Integer> trueAndFalse = Predicates.or(Arrays.asList(TRUE, FALSE));

        assertEvalsToFalse(vacuouslyFalse);
        assertEvalsToTrue(troo);
        assertEvalsToTrue(trueAndFalse);
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_equalityIterable() {
        new EqualsTester()
                .addEqualityGroup(
                        Predicates.or(Arrays.asList(FALSE, NEVER_REACHED)),
                        Predicates.or(Arrays.asList(FALSE, NEVER_REACHED)),
                        Predicates.or(FALSE, NEVER_REACHED))
                .addEqualityGroup(Predicates.or(TRUE, NEVER_REACHED))
                .addEqualityGroup(Predicates.and(FALSE, NEVER_REACHED))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    @SuppressWarnings("unchecked") // varargs
    public void testOr_serializationIterable() {
        Predicate<Integer> pre = Predicates.or(Arrays.asList(TRUE, FALSE));
        Predicate<Integer> post = SerializableTester.reserializeAndAssert(pre);
        assertEquals(pre.apply(0), post.apply(0));
    }

    @SuppressWarnings("unchecked") // varargs
    public void testOr_arrayDefensivelyCopied() {
        Predicate[] array = {Predicates.alwaysFalse()};
        Predicate<Object> predicate = Predicates.or(array);
        assertFalse(predicate.apply(1));
        array[0] = Predicates.alwaysTrue();
        assertFalse(predicate.apply(1));
    }

    public void testOr_listDefensivelyCopied() {
        List<Predicate<Object>> list = newArrayList();
        Predicate<Object> predicate = Predicates.or(list);
        assertFalse(predicate.apply(1));
        list.add(Predicates.alwaysTrue());
        assertFalse(predicate.apply(1));
    }

    public void testOr_iterableDefensivelyCopied() {
        final List<Predicate<Object>> list = newArrayList();
        Iterable<Predicate<Object>> iterable =
                new Iterable<Predicate<Object>>() {
                    @Override
                    public Iterator<Predicate<Object>> iterator() {
                        return list.iterator();
                    }
                };
        Predicate<Object> predicate = Predicates.or(iterable);
        assertFalse(predicate.apply(1));
        list.add(Predicates.alwaysTrue());
        assertFalse(predicate.apply(1));
    }

    /*
     * Tests for Predicates.equalTo(x).
     */

    public void testIsEqualTo_apply() {
        Predicate<Integer> isOne = Predicates.equalTo(1);

        assertTrue(isOne.apply(1));
        assertFalse(isOne.apply(2));
        assertFalse(isOne.apply(null));
    }

    public void testIsEqualTo_equality() {
        new EqualsTester()
                .addEqualityGroup(Predicates.equalTo(1), Predicates.equalTo(1))
                .addEqualityGroup(Predicates.equalTo(2))
                .addEqualityGroup(Predicates.equalTo(null))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testIsEqualTo_serialization() {
        checkSerialization(Predicates.equalTo(1));
    }

    public void testIsEqualToNull_apply() {
        Predicate<Integer> isNull = Predicates.equalTo(null);
        assertTrue(isNull.apply(null));
        assertFalse(isNull.apply(1));
    }

    public void testIsEqualToNull_equality() {
        new EqualsTester()
                .addEqualityGroup(Predicates.equalTo(null), Predicates.equalTo(null))
                .addEqualityGroup(Predicates.equalTo(1))
                .addEqualityGroup(Predicates.equalTo("null"))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testIsEqualToNull_serialization() {
        checkSerialization(Predicates.equalTo(null));
    }

    /**
     * Tests for Predicates.instanceOf(x). TODO: Fix the comment style after fixing annotation
     * stripper to remove comments properly. Currently, all tests before the comments are removed as
     * well.
     */
    @GwtIncompatible // Predicates.instanceOf
    public void testIsInstanceOf_apply() {
        Predicate<Object> isInteger = Predicates.instanceOf(Integer.class);

        assertTrue(isInteger.apply(1));
        assertFalse(isInteger.apply(2.0f));
        assertFalse(isInteger.apply(""));
        assertFalse(isInteger.apply(null));
    }

    @GwtIncompatible // Predicates.instanceOf
    public void testIsInstanceOf_subclass() {
        Predicate<Object> isNumber = Predicates.instanceOf(Number.class);

        assertTrue(isNumber.apply(1));
        assertTrue(isNumber.apply(2.0f));
        assertFalse(isNumber.apply(""));
        assertFalse(isNumber.apply(null));
    }

    @GwtIncompatible // Predicates.instanceOf
    public void testIsInstanceOf_interface() {
        Predicate<Object> isComparable = Predicates.instanceOf(Comparable.class);

        assertTrue(isComparable.apply(1));
        assertTrue(isComparable.apply(2.0f));
        assertTrue(isComparable.apply(""));
        assertFalse(isComparable.apply(null));
    }

    @GwtIncompatible // Predicates.instanceOf
    public void testIsInstanceOf_equality() {
        new EqualsTester()
                .addEqualityGroup(
                        Predicates.instanceOf(Integer.class), Predicates.instanceOf(Integer.class))
                .addEqualityGroup(Predicates.instanceOf(Number.class))
                .addEqualityGroup(Predicates.instanceOf(Float.class))
                .testEquals();
    }

    @GwtIncompatible // Predicates.instanceOf, SerializableTester
    public void testIsInstanceOf_serialization() {
        checkSerialization(Predicates.instanceOf(Integer.class));
    }

    @GwtIncompatible // Predicates.subtypeOf
    public void testSubtypeOf_apply() {
        Predicate<Class<?>> isInteger = Predicates.subtypeOf(Integer.class);

        assertTrue(isInteger.apply(Integer.class));
        assertFalse(isInteger.apply(Float.class));

        try {
            isInteger.apply(null);
            fail();
        } catch (NullPointerException expected) {
        }
    }

    @GwtIncompatible // Predicates.subtypeOf
    public void testSubtypeOf_subclass() {
        Predicate<Class<?>> isNumber = Predicates.subtypeOf(Number.class);

        assertTrue(isNumber.apply(Integer.class));
        assertTrue(isNumber.apply(Float.class));
    }

    @GwtIncompatible // Predicates.subtypeOf
    public void testSubtypeOf_interface() {
        Predicate<Class<?>> isComparable = Predicates.subtypeOf(Comparable.class);

        assertTrue(isComparable.apply(Integer.class));
        assertTrue(isComparable.apply(Float.class));
    }

    @GwtIncompatible // Predicates.subtypeOf
    public void testSubtypeOf_equality() {
        new EqualsTester()
                .addEqualityGroup(Predicates.subtypeOf(Integer.class))
                .addEqualityGroup(Predicates.subtypeOf(Number.class))
                .addEqualityGroup(Predicates.subtypeOf(Float.class))
                .testEquals();
    }

    @GwtIncompatible // Predicates.subtypeOf, SerializableTester
    public void testSubtypeOf_serialization() {
        Predicate<Class<?>> predicate = Predicates.subtypeOf(Integer.class);
        Predicate<Class<?>> reserialized = SerializableTester.reserializeAndAssert(predicate);

        assertEvalsLike(predicate, reserialized, Integer.class);
        assertEvalsLike(predicate, reserialized, Float.class);
        assertEvalsLike(predicate, reserialized, null);
    }

    /*
     * Tests for Predicates.isNull()
     */

    public void testIsNull_apply() {
        Predicate<Integer> isNull = Predicates.isNull();
        assertTrue(isNull.apply(null));
        assertFalse(isNull.apply(1));
    }

    public void testIsNull_equality() {
        new EqualsTester()
                .addEqualityGroup(Predicates.isNull(), Predicates.isNull())
                .addEqualityGroup(Predicates.notNull())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testIsNull_serialization() {
        Predicate<String> pre = Predicates.isNull();
        Predicate<String> post = SerializableTester.reserializeAndAssert(pre);
        assertEquals(pre.apply("foo"), post.apply("foo"));
        assertEquals(pre.apply(null), post.apply(null));
    }

    public void testNotNull_apply() {
        Predicate<Integer> notNull = Predicates.notNull();
        assertFalse(notNull.apply(null));
        assertTrue(notNull.apply(1));
    }

    public void testNotNull_equality() {
        new EqualsTester()
                .addEqualityGroup(Predicates.notNull(), Predicates.notNull())
                .addEqualityGroup(Predicates.isNull())
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testNotNull_serialization() {
        checkSerialization(Predicates.notNull());
    }

    public void testIn_apply() {
        Collection<Integer> nums = Arrays.asList(1, 5);
        Predicate<Integer> isOneOrFive = Predicates.in(nums);

        assertTrue(isOneOrFive.apply(1));
        assertTrue(isOneOrFive.apply(5));
        assertFalse(isOneOrFive.apply(3));
        assertFalse(isOneOrFive.apply(null));
    }

    public void testIn_equality() {
        Collection<Integer> nums = ImmutableSet.of(1, 5);
        Collection<Integer> sameOrder = ImmutableSet.of(1, 5);
        Collection<Integer> differentOrder = ImmutableSet.of(5, 1);
        Collection<Integer> differentNums = ImmutableSet.of(1, 3, 5);

        new EqualsTester()
                .addEqualityGroup(
                        Predicates.in(nums),
                        Predicates.in(nums),
                        Predicates.in(sameOrder),
                        Predicates.in(differentOrder))
                .addEqualityGroup(Predicates.in(differentNums))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testIn_serialization() {
        checkSerialization(Predicates.in(Arrays.asList(1, 2, 3, null)));
    }

    public void testIn_handlesNullPointerException() {
        class CollectionThatThrowsNPE<T> extends ArrayList<T> {
            private static final long serialVersionUID = 1L;

            @Override
            public boolean contains(Object element) {
                Preconditions.checkNotNull(element);
                return super.contains(element);
            }
        }
        Collection<Integer> nums = new CollectionThatThrowsNPE<>();
        Predicate<Integer> isFalse = Predicates.in(nums);
        assertFalse(isFalse.apply(null));
    }

    public void testIn_handlesClassCastException() {
        class CollectionThatThrowsCCE<T> extends ArrayList<T> {
            private static final long serialVersionUID = 1L;

            @Override
            public boolean contains(Object element) {
                throw new ClassCastException("");
            }
        }
        Collection<Integer> nums = new CollectionThatThrowsCCE<>();
        nums.add(3);
        Predicate<Integer> isThree = Predicates.in(nums);
        assertFalse(isThree.apply(3));
    }

    /*
     * Tests that compilation will work when applying explicit types.
     */
    @SuppressWarnings("unused") // compilation test
    public void testIn_compilesWithExplicitSupertype() {
        Collection<Number> nums = ImmutableSet.of();
        Predicate<Number> p1 = Predicates.in(nums);
        Predicate<Object> p2 = Predicates.<Object>in(nums);
        // The next two lines are not expected to compile.
        // Predicate<Integer> p3 = Predicates.in(nums);
        // Predicate<Integer> p4 = Predicates.<Integer>in(nums);
    }

    @GwtIncompatible // NullPointerTester
    public void testNullPointerExceptions() {
        NullPointerTester tester = new NullPointerTester();
        tester.testAllPublicStaticMethods(Predicates.class);
    }

    @SuppressWarnings("unchecked") // varargs
    @GwtIncompatible // SerializbleTester
    public void testCascadingSerialization() throws Exception {
        // Eclipse says Predicate<Integer>; javac says Predicate<Object>.
        Predicate<? super Integer> nasty =
                Predicates.not(
                        Predicates.and(
                                Predicates.or(
                                        Predicates.equalTo((Object) 1),
                                        Predicates.equalTo(null),
                                        Predicates.alwaysFalse(),
                                        Predicates.alwaysTrue(),
                                        Predicates.isNull(),
                                        Predicates.notNull(),
                                        Predicates.in(Arrays.asList(1)))));
        assertEvalsToFalse(nasty);

        Predicate<? super Integer> stillNasty = SerializableTester.reserializeAndAssert(nasty);

        assertEvalsToFalse(stillNasty);
    }

    // enum singleton pattern
    private enum TrimStringFunction implements Function<String, String> {
        INSTANCE;

        @Override
        public String apply(String string) {
            return whitespace().trimFrom(string);
        }
    }

    public void testCompose() {
        Function<String, String> trim = TrimStringFunction.INSTANCE;
        Predicate<String> equalsFoo = Predicates.equalTo("Foo");
        Predicate<String> equalsBar = Predicates.equalTo("Bar");
        Predicate<String> trimEqualsFoo = Predicates.compose(equalsFoo, trim);
        Function<String, String> identity = Functions.identity();

        assertTrue(trimEqualsFoo.apply("Foo"));
        assertTrue(trimEqualsFoo.apply("   Foo   "));
        assertFalse(trimEqualsFoo.apply("Foo-b-que"));

        new EqualsTester()
                .addEqualityGroup(trimEqualsFoo, Predicates.compose(equalsFoo, trim))
                .addEqualityGroup(equalsFoo)
                .addEqualityGroup(trim)
                .addEqualityGroup(Predicates.compose(equalsFoo, identity))
                .addEqualityGroup(Predicates.compose(equalsBar, trim))
                .testEquals();
    }

    @GwtIncompatible // SerializableTester
    public void testComposeSerialization() {
        Function<String, String> trim = TrimStringFunction.INSTANCE;
        Predicate<String> equalsFoo = Predicates.equalTo("Foo");
        Predicate<String> trimEqualsFoo = Predicates.compose(equalsFoo, trim);
        SerializableTester.reserializeAndAssert(trimEqualsFoo);
    }

    /**
     * Tests for Predicates.contains(Pattern) and .containsPattern(String). We assume the regex level
     * works, so there are only trivial tests of that aspect. TODO: Fix comment style once annotation
     * stripper is fixed.
     */
    @GwtIncompatible // Predicates.containsPattern
    public void testContainsPattern_apply() {
        Predicate<CharSequence> isFoobar = Predicates.containsPattern("^Fo.*o.*bar$");
        assertTrue(isFoobar.apply("Foxyzoabcbar"));
        assertFalse(isFoobar.apply("Foobarx"));
    }

    @GwtIncompatible // Predicates.containsPattern
    public void testContains_apply() {
        Predicate<CharSequence> isFoobar = Predicates.contains(Pattern.compile("^Fo.*o.*bar$"));

        assertTrue(isFoobar.apply("Foxyzoabcbar"));
        assertFalse(isFoobar.apply("Foobarx"));
    }

    @GwtIncompatible // NullPointerTester
    public void testContainsPattern_nulls() throws Exception {
        NullPointerTester tester = new NullPointerTester();
        Predicate<CharSequence> isWooString = Predicates.containsPattern("Woo");

        tester.testAllPublicInstanceMethods(isWooString);
    }

    @GwtIncompatible // NullPointerTester
    public void testContains_nulls() throws Exception {
        NullPointerTester tester = new NullPointerTester();
        Predicate<CharSequence> isWooPattern = Predicates.contains(Pattern.compile("Woo"));

        tester.testAllPublicInstanceMethods(isWooPattern);
    }

    @GwtIncompatible // SerializableTester
    public void testContainsPattern_serialization() {
        Predicate<CharSequence> pre = Predicates.containsPattern("foo");
        Predicate<CharSequence> post = SerializableTester.reserializeAndAssert(pre);
        assertEquals(pre.apply("foo"), post.apply("foo"));
    }

    @GwtIncompatible // java.util.regex.Pattern
    public void testContains_equals() {
        new EqualsTester()
                .addEqualityGroup(
                        Predicates.contains(Pattern.compile("foo")), Predicates.containsPattern("foo"))
                .addEqualityGroup(Predicates.contains(Pattern.compile("foo", Pattern.CASE_INSENSITIVE)))
                .addEqualityGroup(Predicates.containsPattern("bar"))
                .testEquals();
    }

    public void assertEqualHashCode(
            Predicate<? super Integer> expected, Predicate<? super Integer> actual) {
        assertEquals(actual + " should hash like " + expected, expected.hashCode(), actual.hashCode());
    }

    public void testHashCodeForBooleanOperations() {
        Predicate<Integer> p1 = Predicates.isNull();
        Predicate<Integer> p2 = isOdd();

        // Make sure that hash codes are not computed per-instance.
        assertEqualHashCode(Predicates.not(p1), Predicates.not(p1));

        assertEqualHashCode(Predicates.and(p1, p2), Predicates.and(p1, p2));

        assertEqualHashCode(Predicates.or(p1, p2), Predicates.or(p1, p2));

        // While not a contractual requirement, we'd like the hash codes for ands
        // & ors of the same predicates to not collide.
        assertTrue(Predicates.and(p1, p2).hashCode() != Predicates.or(p1, p2).hashCode());
    }

    @GwtIncompatible // reflection
    public void testNulls() throws Exception {
        new ClassSanityTester().forAllPublicStaticMethods(Predicates.class).testNulls();
    }

    @GwtIncompatible // reflection
    public void testEqualsAndSerializable() throws Exception {
        new ClassSanityTester().forAllPublicStaticMethods(Predicates.class).testEqualsAndSerializable();
    }

    private static void assertEvalsToTrue(Predicate<? super Integer> predicate) {
        assertTrue(predicate.apply(0));
        assertTrue(predicate.apply(1));
        assertTrue(predicate.apply(null));
    }

    private static void assertEvalsToFalse(Predicate<? super Integer> predicate) {
        assertFalse(predicate.apply(0));
        assertFalse(predicate.apply(1));
        assertFalse(predicate.apply(null));
    }

    private static void assertEvalsLikeOdd(Predicate<? super Integer> predicate) {
        assertEvalsLike(isOdd(), predicate);
    }

    private static void assertEvalsLike(
            Predicate<? super Integer> expected, Predicate<? super Integer> actual) {
        assertEvalsLike(expected, actual, 0);
        assertEvalsLike(expected, actual, 1);
        assertEvalsLike(expected, actual, null);
    }

    private static <T> void assertEvalsLike(
            Predicate<? super T> expected, Predicate<? super T> actual, T input) {
        Boolean expectedResult = null;
        RuntimeException expectedRuntimeException = null;
        try {
            expectedResult = expected.apply(input);
        } catch (RuntimeException e) {
            expectedRuntimeException = e;
        }

        Boolean actualResult = null;
        RuntimeException actualRuntimeException = null;
        try {
            actualResult = actual.apply(input);
        } catch (RuntimeException e) {
            actualRuntimeException = e;
        }

        assertEquals(expectedResult, actualResult);
        if (expectedRuntimeException != null) {
            assertNotNull(actualRuntimeException);
            assertEquals(expectedRuntimeException.getClass(), actualRuntimeException.getClass());
        }
    }

    @GwtIncompatible // SerializableTester
    private static void checkSerialization(Predicate<? super Integer> predicate) {
        Predicate<? super Integer> reserialized = SerializableTester.reserializeAndAssert(predicate);
        assertEvalsLike(predicate, reserialized);
    }
}

7.3Equivalence

import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Equivalence;
import com.google.common.base.Equivalence.Wrapper;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.EquivalenceTester;
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.SerializableTester;
import junit.framework.TestCase;

public class EquivalenceTest extends TestCase {
    
    @SuppressWarnings("unchecked") // varargs
    public void testPairwiseEquivalent() {
        EquivalenceTester.of(Equivalence.equals().<String>pairwise())
                .addEquivalenceGroup(ImmutableList.<String>of())
                .addEquivalenceGroup(ImmutableList.of("a"))
                .addEquivalenceGroup(ImmutableList.of("b"))
                .addEquivalenceGroup(ImmutableList.of("a", "b"), ImmutableList.of("a", "b"))
                .test();
    }

    public void testPairwiseEquivalent_equals() {
        new EqualsTester()
                .addEqualityGroup(Equivalence.equals().pairwise(), Equivalence.equals().pairwise())
                .addEqualityGroup(Equivalence.identity().pairwise())
                .testEquals();
    }

    private enum LengthFunction implements Function<String, Integer> {
        INSTANCE;

        @Override
        public Integer apply(String input) {
            return input.length();
        }
    }

    private static final Equivalence<String> LENGTH_EQUIVALENCE =
            Equivalence.equals().onResultOf(LengthFunction.INSTANCE);

    public void testWrap() {
        new EqualsTester()
                .addEqualityGroup(
                        LENGTH_EQUIVALENCE.wrap("hello"),
                        LENGTH_EQUIVALENCE.wrap("hello"),
                        LENGTH_EQUIVALENCE.wrap("world"))
                .addEqualityGroup(LENGTH_EQUIVALENCE.wrap("hi"), LENGTH_EQUIVALENCE.wrap("yo"))
                .addEqualityGroup(LENGTH_EQUIVALENCE.wrap(null), LENGTH_EQUIVALENCE.wrap(null))
                .addEqualityGroup(Equivalence.equals().wrap("hello"))
                .addEqualityGroup(Equivalence.equals().wrap(null))
                .testEquals();
    }

    public void testWrap_get() {
        String test = "test";
        Wrapper<String> wrapper = LENGTH_EQUIVALENCE.wrap(test);
        assertSame(test, wrapper.get());
    }

    @GwtIncompatible // SerializableTester
    public void testSerialization() {
        SerializableTester.reserializeAndAssert(LENGTH_EQUIVALENCE.wrap("hello"));
        SerializableTester.reserializeAndAssert(Equivalence.equals());
        SerializableTester.reserializeAndAssert(Equivalence.identity());
    }

    private static class IntValue {
        private final int value;

        IntValue(int value) {
            this.value = value;
        }

        @Override
        public String toString() {
            return "value = " + value;
        }
    }

    public void testOnResultOf() {
        EquivalenceTester.of(Equivalence.equals().onResultOf(Functions.toStringFunction()))
                .addEquivalenceGroup(new IntValue(1), new IntValue(1))
                .addEquivalenceGroup(new IntValue(2))
                .test();
    }

    public void testOnResultOf_equals() {
        new EqualsTester()
                .addEqualityGroup(
                        Equivalence.identity().onResultOf(Functions.toStringFunction()),
                        Equivalence.identity().onResultOf(Functions.toStringFunction()))
                .addEqualityGroup(Equivalence.equals().onResultOf(Functions.toStringFunction()))
                .addEqualityGroup(Equivalence.identity().onResultOf(Functions.identity()))
                .testEquals();
    }

    public void testEquivalentTo() {
        Predicate<Object> equalTo1 = Equivalence.equals().equivalentTo("1");
        assertTrue(equalTo1.apply("1"));
        assertFalse(equalTo1.apply("2"));
        assertFalse(equalTo1.apply(null));
        Predicate<Object> isNull = Equivalence.equals().equivalentTo(null);
        assertFalse(isNull.apply("1"));
        assertFalse(isNull.apply("2"));
        assertTrue(isNull.apply(null));

        new EqualsTester()
                .addEqualityGroup(equalTo1, Equivalence.equals().equivalentTo("1"))
                .addEqualityGroup(isNull)
                .addEqualityGroup(Equivalence.identity().equivalentTo("1"))
                .testEquals();
    }

    public void testEqualsEquivalent() {
        EquivalenceTester.of(Equivalence.equals())
                .addEquivalenceGroup(new Integer(42), 42)
                .addEquivalenceGroup("a")
                .test();
    }

    public void testIdentityEquivalent() {
        EquivalenceTester.of(Equivalence.identity())
                .addEquivalenceGroup(new Integer(42))
                .addEquivalenceGroup(new Integer(42))
                .addEquivalenceGroup("a")
                .test();
    }

    public void testEquals() {
        new EqualsTester()
                .addEqualityGroup(Equivalence.equals(), Equivalence.equals())
                .addEqualityGroup(Equivalence.identity(), Equivalence.identity())
                .testEquals();
    }

    @GwtIncompatible // NullPointerTester
    public void testNulls() {
        new NullPointerTester().testAllPublicStaticMethods(Equivalence.class);
        new NullPointerTester().testAllPublicInstanceMethods(Equivalence.equals());
        new NullPointerTester().testAllPublicInstanceMethods(Equivalence.identity());
    }
}

7.4Converter

import com.google.common.base.Converter;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.primitives.Longs;
import com.google.common.testing.EqualsTester;
import com.google.common.testing.SerializableTester;
import junit.framework.TestCase;
import java.util.Iterator;
import java.util.List;

import static com.google.common.base.Functions.toStringFunction;

public class ConverterTest extends TestCase {

    private static final Converter<String, Long> STR_TO_LONG =
            new Converter<String, Long>() {
                @Override
                protected Long doForward(String object) {
                    return Long.valueOf(object);
                }

                @Override
                protected String doBackward(Long object) {
                    return String.valueOf(object);
                }

                @Override
                public String toString() {
                    return "string2long";
                }
            };

    private static final Long LONG_VAL = 12345L;
    private static final String STR_VAL = "12345";

    private static final ImmutableList<String> STRINGS = ImmutableList.of("123", "456");
    private static final ImmutableList<Long> LONGS = ImmutableList.of(123L, 456L);

    public void testConverter() {
        assertEquals(LONG_VAL, STR_TO_LONG.convert(STR_VAL));
        assertEquals(STR_VAL, STR_TO_LONG.reverse().convert(LONG_VAL));

        Iterable<Long> convertedValues = STR_TO_LONG.convertAll(STRINGS);
        assertEquals(LONGS, ImmutableList.copyOf(convertedValues));
    }

    public void testConvertAllIsView() {
        List<String> mutableList = Lists.newArrayList("789", "123");
        Iterable<Long> convertedValues = STR_TO_LONG.convertAll(mutableList);
        assertEquals(ImmutableList.of(789L, 123L), ImmutableList.copyOf(convertedValues));

        Iterator<Long> iterator = convertedValues.iterator();
        iterator.next();
        iterator.remove();
        assertEquals(ImmutableList.of("123"), mutableList);
    }

    public void testReverse() {
        Converter<Long, String> reverseConverter = STR_TO_LONG.reverse();

        assertEquals(STR_VAL, reverseConverter.convert(LONG_VAL));
        assertEquals(LONG_VAL, reverseConverter.reverse().convert(STR_VAL));

        Iterable<String> convertedValues = reverseConverter.convertAll(LONGS);
        assertEquals(STRINGS, ImmutableList.copyOf(convertedValues));

        assertSame(STR_TO_LONG, reverseConverter.reverse());

        assertEquals("string2long.reverse()", reverseConverter.toString());

        new EqualsTester()
                .addEqualityGroup(STR_TO_LONG, STR_TO_LONG.reverse().reverse())
                .addEqualityGroup(STR_TO_LONG.reverse(), STR_TO_LONG.reverse())
                .testEquals();
    }

    public void testReverseReverse() {
        Converter<String, Long> converter = STR_TO_LONG;
        assertEquals(converter, converter.reverse().reverse());
    }

    public void testApply() {
        assertEquals(LONG_VAL, STR_TO_LONG.apply(STR_VAL));
    }

    private static class StringWrapper {
        private final String value;

        public StringWrapper(String value) {
            this.value = value;
        }
    }

    public void testAndThen() {
        Converter<StringWrapper, String> first =
                new Converter<StringWrapper, String>() {
                    @Override
                    protected String doForward(StringWrapper object) {
                        return object.value;
                    }

                    @Override
                    protected StringWrapper doBackward(String object) {
                        return new StringWrapper(object);
                    }

                    @Override
                    public String toString() {
                        return "StringWrapper";
                    }
                };

        Converter<StringWrapper, Long> converter = first.andThen(STR_TO_LONG);

        assertEquals(LONG_VAL, converter.convert(new StringWrapper(STR_VAL)));
        assertEquals(STR_VAL, converter.reverse().convert(LONG_VAL).value);

        assertEquals("StringWrapper.andThen(string2long)", converter.toString());

        assertEquals(first.andThen(STR_TO_LONG), first.andThen(STR_TO_LONG));
    }

    public void testIdentityConverter() {
        Converter<String, String> stringIdentityConverter = Converter.identity();

        assertSame(stringIdentityConverter, stringIdentityConverter.reverse());
        assertSame(STR_TO_LONG, stringIdentityConverter.andThen(STR_TO_LONG));

        assertSame(STR_VAL, stringIdentityConverter.convert(STR_VAL));
        assertSame(STR_VAL, stringIdentityConverter.reverse().convert(STR_VAL));

        assertEquals("Converter.identity()", stringIdentityConverter.toString());

        assertSame(Converter.identity(), Converter.identity());
    }

    public void testFrom() {
        Function<String, Integer> forward =
                new Function<String, Integer>() {
                    @Override
                    public Integer apply(String input) {
                        return Integer.parseInt(input);
                    }
                };
        Function<Object, String> backward = toStringFunction();

        Converter<String, Number> converter = Converter.<String, Number>from(forward, backward);

        assertNull(converter.convert(null));
        assertNull(converter.reverse().convert(null));

        assertEquals((Integer) 5, converter.convert("5"));
        assertEquals("5", converter.reverse().convert(5));
    }

    public void testSerialization_identity() {
        Converter<String, String> identityConverter = Converter.identity();
        SerializableTester.reserializeAndAssert(identityConverter);
    }

    public void testSerialization_reverse() {
        Converter<Long, String> reverseConverter = Longs.stringConverter().reverse();
        SerializableTester.reserializeAndAssert(reverseConverter);
    }

    public void testSerialization_andThen() {
        Converter<String, Long> converterA = Longs.stringConverter();
        Converter<Long, String> reverseConverter = Longs.stringConverter().reverse();
        Converter<String, String> composedConverter = converterA.andThen(reverseConverter);
        SerializableTester.reserializeAndAssert(composedConverter);
    }

    public void testSerialization_from() {
        Converter<String, String> dumb = Converter.from(toStringFunction(), toStringFunction());
        SerializableTester.reserializeAndAssert(dumb);
    }

}

7.5Supplier

import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.Lists;
import com.google.common.testing.ClassSanityTester;
import com.google.common.testing.EqualsTester;
import junit.framework.TestCase;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import static com.google.common.testing.SerializableTester.reserialize;
import static com.google.common.truth.Truth.assertThat;

public class SuppliersTest extends TestCase {

    static class CountingSupplier implements Supplier<Integer> {
        int calls = 0;

        @Override
        public Integer get() {
            calls++;
            return calls * 10;
        }

        @Override
        public String toString() {
            return "CountingSupplier";
        }
    }

    static class ThrowingSupplier implements Supplier<Integer> {
        @Override
        public Integer get() {
            throw new NullPointerException();
        }
    }

    static class SerializableCountingSupplier extends CountingSupplier implements Serializable {
        private static final long serialVersionUID = 0L;
    }

    static class SerializableThrowingSupplier extends ThrowingSupplier implements Serializable {
        private static final long serialVersionUID = 0L;
    }

    static void checkMemoize(CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier) {
        // the underlying supplier hasn't executed yet
        assertEquals(0, countingSupplier.calls);

        assertEquals(10, (int) memoizedSupplier.get());

        // now it has
        assertEquals(1, countingSupplier.calls);

        assertEquals(10, (int) memoizedSupplier.get());

        // it still should only have executed once due to memoization
        assertEquals(1, countingSupplier.calls);
    }

    public void testMemoize() {
        memoizeTest(new CountingSupplier());
        memoizeTest(new SerializableCountingSupplier());
    }

    private void memoizeTest(CountingSupplier countingSupplier) {
        Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
        checkMemoize(countingSupplier, memoizedSupplier);
    }

    public void testMemoize_redudantly() {
        memoize_redudantlyTest(new CountingSupplier());
        memoize_redudantlyTest(new SerializableCountingSupplier());
    }

    private void memoize_redudantlyTest(CountingSupplier countingSupplier) {
        Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
        assertSame(memoizedSupplier, Suppliers.memoize(memoizedSupplier));
    }

    public void testMemoizeExceptionThrown() {
        memoizeExceptionThrownTest(new ThrowingSupplier());
        memoizeExceptionThrownTest(new SerializableThrowingSupplier());
    }

    private void memoizeExceptionThrownTest(ThrowingSupplier throwingSupplier) {
        Supplier<Integer> memoizedSupplier = Suppliers.memoize(throwingSupplier);
        // call get() twice to make sure that memoization doesn't interfere
        // with throwing the exception
        for (int i = 0; i < 2; i++) {
            try {
                memoizedSupplier.get();
                fail("failed to throw NullPointerException");
            } catch (NullPointerException e) {
                // this is what should happen
            }
        }
    }

    @GwtIncompatible // SerializableTester
    public void testMemoizeNonSerializable() throws Exception {
        CountingSupplier countingSupplier = new CountingSupplier();
        Supplier<Integer> memoizedSupplier = Suppliers.memoize(countingSupplier);
        assertThat(memoizedSupplier.toString()).isEqualTo("Suppliers.memoize(CountingSupplier)");
        checkMemoize(countingSupplier, memoizedSupplier);
        // Calls to the original memoized supplier shouldn't affect its copy.
        memoizedSupplier.get();
        assertThat(memoizedSupplier.toString())
                .isEqualTo("Suppliers.memoize(<supplier that returned 10>)");

        // Should get an exception when we try to serialize.
        try {
            reserialize(memoizedSupplier);
            fail();
        } catch (RuntimeException ex) {
            assertThat(ex).hasCauseThat().isInstanceOf(java.io.NotSerializableException.class);
        }
    }

    public void testCompose() {
        Supplier<Integer> fiveSupplier =
                new Supplier<Integer>() {
                    @Override
                    public Integer get() {
                        return 5;
                    }
                };

        Function<Number, Integer> intValueFunction =
                new Function<Number, Integer>() {
                    @Override
                    public Integer apply(Number x) {
                        return x.intValue();
                    }
                };

        Supplier<Integer> squareSupplier = Suppliers.compose(intValueFunction, fiveSupplier);

        assertEquals(Integer.valueOf(5), squareSupplier.get());
    }

    public void testComposeWithLists() {
        Supplier<ArrayList<Integer>> listSupplier =
                new Supplier<ArrayList<Integer>>() {
                    @Override
                    public ArrayList<Integer> get() {
                        return Lists.newArrayList(0);
                    }
                };

        Function<List<Integer>, List<Integer>> addElementFunction =
                new Function<List<Integer>, List<Integer>>() {
                    @Override
                    public List<Integer> apply(List<Integer> list) {
                        ArrayList<Integer> result = Lists.newArrayList(list);
                        result.add(1);
                        return result;
                    }
                };

        Supplier<List<Integer>> addSupplier = Suppliers.compose(addElementFunction, listSupplier);

        List<Integer> result = addSupplier.get();
        assertEquals(Integer.valueOf(0), result.get(0));
        assertEquals(Integer.valueOf(1), result.get(1));
    }

    @GwtIncompatible // Thread.sleep
    public void testMemoizeWithExpiration() throws InterruptedException {
        CountingSupplier countingSupplier = new CountingSupplier();

        Supplier<Integer> memoizedSupplier =
                Suppliers.memoizeWithExpiration(countingSupplier, 75, TimeUnit.MILLISECONDS);

        checkExpiration(countingSupplier, memoizedSupplier);
    }

    @GwtIncompatible // Thread.sleep
    private void checkExpiration(
            CountingSupplier countingSupplier, Supplier<Integer> memoizedSupplier)
            throws InterruptedException {
        // the underlying supplier hasn't executed yet
        assertEquals(0, countingSupplier.calls);

        assertEquals(10, (int) memoizedSupplier.get());
        // now it has
        assertEquals(1, countingSupplier.calls);

        assertEquals(10, (int) memoizedSupplier.get());
        // it still should only have executed once due to memoization
        assertEquals(1, countingSupplier.calls);

        Thread.sleep(150);

        assertEquals(20, (int) memoizedSupplier.get());
        // old value expired
        assertEquals(2, countingSupplier.calls);

        assertEquals(20, (int) memoizedSupplier.get());
        // it still should only have executed twice due to memoization
        assertEquals(2, countingSupplier.calls);
    }

    public void testOfInstanceSuppliesSameInstance() {
        Object toBeSupplied = new Object();
        Supplier<Object> objectSupplier = Suppliers.ofInstance(toBeSupplied);
        assertSame(toBeSupplied, objectSupplier.get());
        assertSame(toBeSupplied, objectSupplier.get()); // idempotent
    }

    public void testOfInstanceSuppliesNull() {
        Supplier<Integer> nullSupplier = Suppliers.ofInstance(null);
        assertNull(nullSupplier.get());
    }

    @GwtIncompatible // Thread

    public void testExpiringMemoizedSupplierThreadSafe() throws Throwable {
        Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
                new Function<Supplier<Boolean>, Supplier<Boolean>>() {
                    @Override
                    public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
                        return Suppliers.memoizeWithExpiration(supplier, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
                    }
                };
        testSupplierThreadSafe(memoizer);
    }

    @GwtIncompatible // Thread

    public void testMemoizedSupplierThreadSafe() throws Throwable {
        Function<Supplier<Boolean>, Supplier<Boolean>> memoizer =
                new Function<Supplier<Boolean>, Supplier<Boolean>>() {
                    @Override
                    public Supplier<Boolean> apply(Supplier<Boolean> supplier) {
                        return Suppliers.memoize(supplier);
                    }
                };
        testSupplierThreadSafe(memoizer);
    }

    @GwtIncompatible // Thread
    public void testSupplierThreadSafe(Function<Supplier<Boolean>, Supplier<Boolean>> memoizer)
            throws Throwable {
        final AtomicInteger count = new AtomicInteger(0);
        final AtomicReference<Throwable> thrown = new AtomicReference<>(null);
        final int numThreads = 3;
        final Thread[] threads = new Thread[numThreads];
        final long timeout = TimeUnit.SECONDS.toNanos(60);

        final Supplier<Boolean> supplier =
                new Supplier<Boolean>() {
                    boolean isWaiting(Thread thread) {
                        switch (thread.getState()) {
                            case BLOCKED:
                            case WAITING:
                            case TIMED_WAITING:
                                return true;
                            default:
                                return false;
                        }
                    }

                    int waitingThreads() {
                        int waitingThreads = 0;
                        for (Thread thread : threads) {
                            if (isWaiting(thread)) {
                                waitingThreads++;
                            }
                        }
                        return waitingThreads;
                    }

                    @Override
                    public Boolean get() {
                        // Check that this method is called exactly once, by the first
                        // thread to synchronize.
                        long t0 = System.nanoTime();
                        while (waitingThreads() != numThreads - 1) {
                            if (System.nanoTime() - t0 > timeout) {
                                thrown.set(
                                        new TimeoutException(
                                                "timed out waiting for other threads to block"
                                                        + " synchronizing on supplier"));
                                break;
                            }
                            Thread.yield();
                        }
                        count.getAndIncrement();
                        return Boolean.TRUE;
                    }
                };

        final Supplier<Boolean> memoizedSupplier = memoizer.apply(supplier);

        for (int i = 0; i < numThreads; i++) {
            threads[i] =
                    new Thread() {
                        @Override
                        public void run() {
                            assertSame(Boolean.TRUE, memoizedSupplier.get());
                        }
                    };
        }
        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }

        if (thrown.get() != null) {
            throw thrown.get();
        }
        assertEquals(1, count.get());
    }

    @GwtIncompatible // Thread

    public void testSynchronizedSupplierThreadSafe() throws InterruptedException {
        final Supplier<Integer> nonThreadSafe =
                new Supplier<Integer>() {
                    int counter = 0;

                    @Override
                    public Integer get() {
                        int nextValue = counter + 1;
                        Thread.yield();
                        counter = nextValue;
                        return counter;
                    }
                };

        final int numThreads = 10;
        final int iterations = 1000;
        Thread[] threads = new Thread[numThreads];
        for (int i = 0; i < numThreads; i++) {
            threads[i] =
                    new Thread() {
                        @Override
                        public void run() {
                            for (int j = 0; j < iterations; j++) {
                                Suppliers.synchronizedSupplier(nonThreadSafe).get();
                            }
                        }
                    };
        }
        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }

        assertEquals(numThreads * iterations + 1, (int) nonThreadSafe.get());
    }

    public void testSupplierFunction() {
        Supplier<Integer> supplier = Suppliers.ofInstance(14);
        Function<Supplier<Integer>, Integer> supplierFunction = Suppliers.supplierFunction();

        assertEquals(14, (int) supplierFunction.apply(supplier));
    }

    @GwtIncompatible // SerializationTester
    public void testSerialization() {
        assertEquals(Integer.valueOf(5), reserialize(Suppliers.ofInstance(5)).get());
        assertEquals(
                Integer.valueOf(5),
                reserialize(Suppliers.compose(Functions.identity(), Suppliers.ofInstance(5))).get());
        assertEquals(Integer.valueOf(5), reserialize(Suppliers.memoize(Suppliers.ofInstance(5))).get());
        assertEquals(
                Integer.valueOf(5),
                reserialize(Suppliers.memoizeWithExpiration(Suppliers.ofInstance(5), 30, TimeUnit.SECONDS))
                        .get());
        assertEquals(
                Integer.valueOf(5),
                reserialize(Suppliers.synchronizedSupplier(Suppliers.ofInstance(5))).get());
    }

    @GwtIncompatible // reflection
    public void testSuppliersNullChecks() throws Exception {
        new ClassSanityTester().forAllPublicStaticMethods(Suppliers.class).testNulls();
    }

    @GwtIncompatible // reflection
    public void testSuppliersSerializable() throws Exception {
        new ClassSanityTester().forAllPublicStaticMethods(Suppliers.class).testSerializable();
    }

    public void testOfInstance_equals() {
        new EqualsTester()
                .addEqualityGroup(Suppliers.ofInstance("foo"), Suppliers.ofInstance("foo"))
                .addEqualityGroup(Suppliers.ofInstance("bar"))
                .testEquals();
    }

    public void testCompose_equals() {
        new EqualsTester()
                .addEqualityGroup(
                        Suppliers.compose(Functions.constant(1), Suppliers.ofInstance("foo")),
                        Suppliers.compose(Functions.constant(1), Suppliers.ofInstance("foo")))
                .addEqualityGroup(Suppliers.compose(Functions.constant(2), Suppliers.ofInstance("foo")))
                .addEqualityGroup(Suppliers.compose(Functions.constant(1), Suppliers.ofInstance("bar")))
                .testEquals();
    }
}

本文參考:
FunctionalExplained
guava-tests-base

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章