2023-08-17  阅读(40)
原文作者:LifeIsForSharing 原文地址:https://solang.blog.csdn.net/article/details/105101316

Guava项目包含一些我们在基于Java的项目中依赖的Google核心库:集合[collections]、缓存[caching]、原生类型支持[primitives support]、并发库[concurrency libraries]、通用注解[common annotations]、字符串处理[string processing]、I/O等。这些工具中的每一种确实每天都会被Google员工用于生产服务中。

但是,通过Javadoc并不总是最有效的去学习如何充分使用库。在这里,我们尝试提供一些Guava最流行和最强大的功能的可读且令人愉快的解释说明。

该Wiki正在开发中,部分内容可能仍在建设中。

基本工具:让使用Java语言更加愉快。

  • 使用和避免使用nullnull可能会造成歧义,导致混乱的错误,有时甚至是令人不快的。许多Guava实用工具拒绝null并快速失败,而不是盲目地接受它们。
  • 前置条件:更轻松地测试方法的前置条件。
  • 通用对象方法:简化实现Object的方法,例如hashCode()toString()
  • 排序:Guava功能强大的"流利Comparator"类。Guava’s powerful "fluent Comparator" class.
  • 异常:简化了异常和错误的传播与检查。

1.使用和避免使用null

1.1Optional

程序员使用null的许多情况是表示某种缺席:也许在可能有值、无值或找不到值的地方。例如,当找不到键值时,Map.get返回null

Optional<T>是用非空值替换可空的T引用的一种方法。Optional可以包含非空T引用(在这种情况下,我们称该引用为“存在”),也可以不包含任何内容(在这种情况下,我们称该引用为“不存在”)。它从来不说“包含null”。

    Optional<Integer> possible = Optional.of(5);
    possible.isPresent(); // returns true
    possible.get(); // returns 5

Optional 打算直接模拟其它编程环境中的任何现有“option”或“maybe”构造,尽管它可能有一些相似之处。

我们在这里列出了一些最常见的Optional操作。

1.1.1创建Optional

这些都是Optional上的静态方法。

方法 描述
Optional.of(T) 使Optional包含给定的非null值,或者在null上快速失败。
Optional.absent() 返回某种引用缺失的Optional。
Optional.fromNullable(T) 将给定的可能为null的引用转换为Optional,将non-null视为存在,将null视为不存在。

1.1.2查询方法

这些都是基于特定Optional<T>值的非静态方法。

方法 描述
booleanisPresent() 如果此Optional包含非空实例,则返回true。
Tget() 返回包含的T实例,该实例必须存在;否则,抛出IllegalStateException。
Tor(T) 返回此Optional中的当前值,如果没有,则返回指定的默认值。
TorNull() 返回此Optional中的当前值,如果没有,则返回null。fromNullable的逆运算。
SetasSet() 如果存在实例,则返回Optional所包含此实例的不可变单例Set,否则返回一个空的不可变Set。

Optional提供了除这些方法之外的更多便捷实用方法。有关详细信息,请查阅Javadoc。

1.1.3重点是什么?

除了为null命名而提高了可读性之外,Optional的最大优点是它的防白痴。如果你要完全编译程序,它会迫使你积极考虑不存在的情况,因为你必须主动打开Optional并解决该情况。令人不安的是,Null很容易让人忘记一些事情,尽管FindBugs有所帮助,但我们认为它没有很好的解决问题。

当你 返回 的值可能存在或不存在时,这一点尤其重要。你(和其他人)更可能忘记在实现other.method时,other.method(a, b)可能返回null值,而你可能忘记了a可能为null。返回Optional使调用者无法忘记这种情况,因为调用者必须自己打开对象去编译代码。

1.2便利方法

每当你希望将null值替换为某些默认值时,请使用MoreObjects.firstNonNull(T, T)。就像方法名称所暗示的那样,如果两个输入都为null,则它会由于NullPointerException而快速失败。如果你使用的是Optional,则有更好的选择——例如,first.or(second)

字符串中提供了一些处理可能为空的字符串值的方法。具体来说,我们提供恰当的名称:

我们想强调的是,这些方法主要用于与使null字符串和空字符串等同看待的令人不愉快的API联合使用。每次你编写将null字符串和空字符串合在一起的代码时,Guava团队都会哭泣。(如果null字符串和空字符串表示的是完全不同的东西,那会更好,但是将它们视为同一种东西是一种令人不安的常见代码坏味道)

1.3使用示例

    import com.google.common.base.Functions;
    import com.google.common.base.Optional;
    import com.google.common.base.Supplier;
    import com.google.common.base.Suppliers;
    import com.google.common.collect.ImmutableList;
    import junit.framework.TestCase;
    import java.util.Collections;
    import java.util.List;
    import java.util.Set;
    
    import static com.google.common.truth.Truth.assertThat;
    
    public final class OptionalTest extends TestCase {
        public void testToJavaUtil_static() {
            assertNull(Optional.toJavaUtil(null));
            assertEquals(java.util.Optional.empty(), Optional.toJavaUtil(Optional.absent()));
            assertEquals(java.util.Optional.of("abc"), Optional.toJavaUtil(Optional.of("abc")));
        }
    
        public void testToJavaUtil_instance() {
            assertEquals(java.util.Optional.empty(), Optional.absent().toJavaUtil());
            assertEquals(java.util.Optional.of("abc"), Optional.of("abc").toJavaUtil());
        }
    
        public void testFromJavaUtil() {
            assertNull(Optional.fromJavaUtil(null));
            assertEquals(Optional.absent(), Optional.fromJavaUtil(java.util.Optional.empty()));
            assertEquals(Optional.of("abc"), Optional.fromJavaUtil(java.util.Optional.of("abc")));
        }
    
        public void testAbsent() {
            Optional<String> optionalName = Optional.absent();
            assertFalse(optionalName.isPresent());
        }
    
        public void testOf() {
            assertEquals("training", Optional.of("training").get());
        }
    
        public void testOf_null() {
            try {
                Optional.of(null);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testFromNullable() {
            Optional<String> optionalName = Optional.fromNullable("bob");
            assertEquals("bob", optionalName.get());
        }
    
        public void testFromNullable_null() {
            // not promised by spec, but easier to test
            assertSame(Optional.absent(), Optional.fromNullable(null));
        }
    
        public void testIsPresent_no() {
            assertFalse(Optional.absent().isPresent());
        }
    
        public void testIsPresent_yes() {
            assertTrue(Optional.of("training").isPresent());
        }
    
        public void testGet_absent() {
            Optional<String> optional = Optional.absent();
            try {
                optional.get();
                fail();
            } catch (IllegalStateException expected) {
            }
        }
    
        public void testGet_present() {
            assertEquals("training", Optional.of("training").get());
        }
    
        public void testOr_T_present() {
            assertEquals("a", Optional.of("a").or("default"));
        }
    
        public void testOr_T_absent() {
            assertEquals("default", Optional.absent().or("default"));
        }
    
        public void testOr_supplier_present() {
            assertEquals("a", Optional.of("a").or(Suppliers.ofInstance("fallback")));
        }
    
        public void testOr_supplier_absent() {
            assertEquals("fallback", Optional.absent().or(Suppliers.ofInstance("fallback")));
        }
    
        public void testOr_nullSupplier_absent() {
            Supplier<Object> nullSupplier = Suppliers.ofInstance(null);
            Optional<Object> absentOptional = Optional.absent();
            try {
                absentOptional.or(nullSupplier);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testOr_nullSupplier_present() {
            Supplier<String> nullSupplier = Suppliers.ofInstance(null);
            assertEquals("a", Optional.of("a").or(nullSupplier));
        }
    
        public void testOr_Optional_present() {
            assertEquals(Optional.of("a"), Optional.of("a").or(Optional.of("fallback")));
        }
    
        public void testOr_Optional_absent() {
            assertEquals(Optional.of("fallback"), Optional.absent().or(Optional.of("fallback")));
        }
    
        public void testOrNull_present() {
            assertEquals("a", Optional.of("a").orNull());
        }
    
        public void testOrNull_absent() {
            assertNull(Optional.absent().orNull());
        }
    
        public void testAsSet_present() {
            Set<String> expected = Collections.singleton("a");
            assertEquals(expected, Optional.of("a").asSet());
        }
    
        public void testAsSet_absent() {
            assertTrue("Returned set should be empty", Optional.absent().asSet().isEmpty());
        }
    
        public void testAsSet_presentIsImmutable() {
            Set<String> presentAsSet = Optional.of("a").asSet();
            try {
                presentAsSet.add("b");
                fail();
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        public void testAsSet_absentIsImmutable() {
            Set<Object> absentAsSet = Optional.absent().asSet();
            try {
                absentAsSet.add("foo");
                fail();
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        public void testTransform_absent() {
            assertEquals(Optional.absent(), Optional.absent().transform(Functions.identity()));
            assertEquals(Optional.absent(), Optional.absent().transform(Functions.toStringFunction()));
        }
    
        public void testTransform_presentIdentity() {
            assertEquals(Optional.of("a"), Optional.of("a").transform(Functions.identity()));
        }
    
        public void testTransform_presentToString() {
            assertEquals(Optional.of("42"), Optional.of(42).transform(Functions.toStringFunction()));
        }
    
        public void testPresentInstances_allPresent() {
            List<Optional<String>> optionals =
                    ImmutableList.of(Optional.of("a"), Optional.of("b"), Optional.of("c"));
            assertThat(Optional.presentInstances(optionals)).containsExactly("a", "b", "c").inOrder();
        }
    
        public void testPresentInstances_allAbsent() {
            List<Optional<Object>> optionals = ImmutableList.of(Optional.absent(), Optional.absent());
            assertThat(Optional.presentInstances(optionals)).isEmpty();
        }
    
        public void testPresentInstances_somePresent() {
            List<Optional<String>> optionals =
                    ImmutableList.of(Optional.of("a"), Optional.<String>absent(), Optional.of("c"));
            assertThat(Optional.presentInstances(optionals)).containsExactly("a", "c").inOrder();
        }
    
        public void testPresentInstances_callingIteratorTwice() {
            List<Optional<String>> optionals =
                    ImmutableList.of(Optional.of("a"), Optional.<String>absent(), Optional.of("c"));
            Iterable<String> onlyPresent = Optional.presentInstances(optionals);
            assertThat(onlyPresent).containsExactly("a", "c").inOrder();
            assertThat(onlyPresent).containsExactly("a", "c").inOrder();
        }
    
        public void testPresentInstances_wildcards() {
            List<Optional<? extends Number>> optionals =
                    ImmutableList.<Optional<? extends Number>>of(Optional.<Double>absent(), Optional.of(2));
            Iterable<Number> onlyPresent = Optional.presentInstances(optionals);
            assertThat(onlyPresent).containsExactly(2);
        }
    
    }

2.前置条件

2.1介绍

Guava提供了许多前置条件检查工具。每个方法有3个变体:

  1. 没有额外参数。抛出异常而不会出现错误消息。

  2. 一个额外的Object参数。抛出异常使用object.toString()作为错误消息。

  3. 一个额外的String参数,带有任意数量的Object对象参数。行为类似于printf,但是为了GWT的兼容性和效率,它仅允许%s指示符。

    • 注意:checkNotNullcheckArgumentcheckState具有大量重载,这些重载采用原始参数和Object参数而不是可变数组的组合——这样,在大多数情况下,上述调用都可以避免原始装箱和可变数组分配

第三种变体的示例:

    checkArgument(i >= 0, "Argument was %s but expected nonnegative", i);
    checkArgument(i < j, "Expected i < j, but %s >= %s", i, j);
方法声明(不包括额外参数) 描述 失败时抛出异常
checkArgument(boolean) 检查boolean是否为true。用于验证方法的参数。 IllegalArgumentException
checkNotNull(T) 检查值是否不为null。直接返回值,因此可以内联使用checkNotNull(value)。 NullPointerException
checkState(boolean) 检查对象的某些状态,而不依赖于方法参数。例如,一个Iterator可能会使用它来检查在调用任何remove之前是否已调用next。 IllegalStateException
checkElementIndex(intindex,intsize) 检查index是否为具有指定大小的列表、字符串或数组的有效元素索引。元素索引的范围可以从0(含0)到size (不含) 。不直接通过列表、字符串或数组;而是只要通过它的size。返回index。 IndexOutOfBoundsException
checkPositionIndex(intindex,intsize) 检查index是否为具有指定大小的列表、字符串或数组的有效位置索引。位置索引的范围可以是0(含0)到size (含) 。不直接通过列表、字符串或数组;而是只要通过它的size。返回index。 IndexOutOfBoundsException
checkPositionIndexes(intstart,intend,intsize) 检查[start,end)是否为具有指定大小的列表、字符串或数组的有效子范围。带有自己的错误消息。 IndexOutOfBoundsException

出于几个原因,我们倾向于使用我们自己的先决条件检查,而不是Apache Commons中的类似工具。简要地:

  • 静态导入后,Guava方法清晰明了。checkNotNull可以清楚地说明正在执行的操作以及将抛出的异常。
  • checkNotNull在验证后返回其参数,从而允许在构造函数中使用简单的一列式:this.field = checkNotNull(field);
  • 简单的可变参数"printf样式"异常消息。(这也是我们建议继续对Objects.requireNonNull使用checkNotNull的原因)

我们建议你将前置条件分成不同的行,这可以帮助你确定调试时哪个前置条件失败。此外,你应该提供有用的错误消息,当每项检查都在自己的行上时,会更容易。

2.2使用示例

    import com.google.common.base.Preconditions;
    import junit.framework.AssertionFailedError;
    import junit.framework.TestCase;
    
    import static com.google.common.truth.Truth.assertThat;
    
    public class PreconditionsTest extends TestCase {
        public void testCheckArgument_simple_success() {
            Preconditions.checkArgument(true);
        }
    
        public void testCheckArgument_simple_failure() {
            try {
                Preconditions.checkArgument(false);
                fail("no exception thrown");
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testCheckArgument_simpleMessage_success() {
            Preconditions.checkArgument(true, IGNORE_ME);
        }
    
        public void testCheckArgument_simpleMessage_failure() {
            try {
                Preconditions.checkArgument(false, new Message());
                fail("no exception thrown");
            } catch (IllegalArgumentException expected) {
                verifySimpleMessage(expected);
            }
        }
    
        public void testCheckArgument_nullMessage_failure() {
            try {
                Preconditions.checkArgument(false, null);
                fail("no exception thrown");
            } catch (IllegalArgumentException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("null");
            }
        }
    
        public void testCheckArgument_nullMessageWithArgs_failure() {
            try {
                Preconditions.checkArgument(false, null, "b", "d");
                fail("no exception thrown");
            } catch (IllegalArgumentException e) {
                assertThat(e).hasMessageThat().isEqualTo("null [b, d]");
            }
        }
    
        public void testCheckArgument_nullArgs_failure() {
            try {
                Preconditions.checkArgument(false, "A %s C %s E", null, null);
                fail("no exception thrown");
            } catch (IllegalArgumentException e) {
                assertThat(e).hasMessageThat().isEqualTo("A null C null E");
            }
        }
    
        public void testCheckArgument_notEnoughArgs_failure() {
            try {
                Preconditions.checkArgument(false, "A %s C %s E", "b");
                fail("no exception thrown");
            } catch (IllegalArgumentException e) {
                assertThat(e).hasMessageThat().isEqualTo("A b C %s E");
            }
        }
    
        public void testCheckArgument_tooManyArgs_failure() {
            try {
                Preconditions.checkArgument(false, "A %s C %s E", "b", "d", "f");
                fail("no exception thrown");
            } catch (IllegalArgumentException e) {
                assertThat(e).hasMessageThat().isEqualTo("A b C d E [f]");
            }
        }
    
        public void testCheckArgument_singleNullArg_failure() {
            try {
                Preconditions.checkArgument(false, "A %s C", (Object) null);
                fail("no exception thrown");
            } catch (IllegalArgumentException e) {
                assertThat(e).hasMessageThat().isEqualTo("A null C");
            }
        }
    
        public void testCheckArgument_singleNullArray_failure() {
            try {
                Preconditions.checkArgument(false, "A %s C", (Object[]) null);
                fail("no exception thrown");
            } catch (IllegalArgumentException e) {
                assertThat(e).hasMessageThat().isEqualTo("A (Object[])null C");
            }
        }
    
        public void testCheckArgument_complexMessage_success() {
            Preconditions.checkArgument(true, "%s", IGNORE_ME);
        }
    
        public void testCheckArgument_complexMessage_failure() {
            try {
                Preconditions.checkArgument(false, FORMAT, 5);
                fail("no exception thrown");
            } catch (IllegalArgumentException expected) {
                verifyComplexMessage(expected);
            }
        }
    
        public void testCheckState_simple_success() {
            Preconditions.checkState(true);
        }
    
        public void testCheckState_simple_failure() {
            try {
                Preconditions.checkState(false);
                fail("no exception thrown");
            } catch (IllegalStateException expected) {
            }
        }
    
        public void testCheckState_simpleMessage_success() {
            Preconditions.checkState(true, IGNORE_ME);
        }
    
        public void testCheckState_simpleMessage_failure() {
            try {
                Preconditions.checkState(false, new Message());
                fail("no exception thrown");
            } catch (IllegalStateException expected) {
                verifySimpleMessage(expected);
            }
        }
    
        public void testCheckState_nullMessage_failure() {
            try {
                Preconditions.checkState(false, null);
                fail("no exception thrown");
            } catch (IllegalStateException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("null");
            }
        }
    
        public void testCheckState_complexMessage_success() {
            Preconditions.checkState(true, "%s", IGNORE_ME);
        }
    
        public void testCheckState_complexMessage_failure() {
            try {
                Preconditions.checkState(false, FORMAT, 5);
                fail("no exception thrown");
            } catch (IllegalStateException expected) {
                verifyComplexMessage(expected);
            }
        }
    
        private static final String NON_NULL_STRING = "foo";
    
        public void testCheckNotNull_simple_success() {
            String result = Preconditions.checkNotNull(NON_NULL_STRING);
            assertSame(NON_NULL_STRING, result);
        }
    
        public void testCheckNotNull_simple_failure() {
            try {
                Preconditions.checkNotNull(null);
                fail("no exception thrown");
            } catch (NullPointerException expected) {
            }
        }
    
        public void testCheckNotNull_simpleMessage_success() {
            String result = Preconditions.checkNotNull(NON_NULL_STRING, IGNORE_ME);
            assertSame(NON_NULL_STRING, result);
        }
    
        public void testCheckNotNull_simpleMessage_failure() {
            try {
                Preconditions.checkNotNull(null, new Message());
                fail("no exception thrown");
            } catch (NullPointerException expected) {
                verifySimpleMessage(expected);
            }
        }
    
        public void testCheckNotNull_complexMessage_success() {
            String result = Preconditions.checkNotNull(NON_NULL_STRING, "%s", IGNORE_ME);
            assertSame(NON_NULL_STRING, result);
        }
    
        public void testCheckNotNull_complexMessage_failure() {
            try {
                Preconditions.checkNotNull(null, FORMAT, 5);
                fail("no exception thrown");
            } catch (NullPointerException expected) {
                verifyComplexMessage(expected);
            }
        }
    
        public void testCheckElementIndex_ok() {
            assertEquals(0, Preconditions.checkElementIndex(0, 1));
            assertEquals(0, Preconditions.checkElementIndex(0, 2));
            assertEquals(1, Preconditions.checkElementIndex(1, 2));
        }
    
        public void testCheckElementIndex_badSize() {
            try {
                Preconditions.checkElementIndex(1, -1);
                fail();
            } catch (IllegalArgumentException expected) {
                // don't care what the message text is, as this is an invalid usage of
                // the Preconditions class, unlike all the other exceptions it throws
            }
        }
    
        public void testCheckElementIndex_negative() {
            try {
                Preconditions.checkElementIndex(-1, 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative");
            }
        }
    
        public void testCheckElementIndex_tooHigh() {
            try {
                Preconditions.checkElementIndex(1, 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("index (1) must be less than size (1)");
            }
        }
    
        public void testCheckElementIndex_withDesc_negative() {
            try {
                Preconditions.checkElementIndex(-1, 1, "foo");
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative");
            }
        }
    
        public void testCheckElementIndex_withDesc_tooHigh() {
            try {
                Preconditions.checkElementIndex(1, 1, "foo");
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("foo (1) must be less than size (1)");
            }
        }
    
        public void testCheckPositionIndex_ok() {
            assertEquals(0, Preconditions.checkPositionIndex(0, 0));
            assertEquals(0, Preconditions.checkPositionIndex(0, 1));
            assertEquals(1, Preconditions.checkPositionIndex(1, 1));
        }
    
        public void testCheckPositionIndex_badSize() {
            try {
                Preconditions.checkPositionIndex(1, -1);
                fail();
            } catch (IllegalArgumentException expected) {
                // don't care what the message text is, as this is an invalid usage of
                // the Preconditions class, unlike all the other exceptions it throws
            }
        }
    
        public void testCheckPositionIndex_negative() {
            try {
                Preconditions.checkPositionIndex(-1, 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("index (-1) must not be negative");
            }
        }
    
        public void testCheckPositionIndex_tooHigh() {
            try {
                Preconditions.checkPositionIndex(2, 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected)
                        .hasMessageThat()
                        .isEqualTo("index (2) must not be greater than size (1)");
            }
        }
    
        public void testCheckPositionIndex_withDesc_negative() {
            try {
                Preconditions.checkPositionIndex(-1, 1, "foo");
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("foo (-1) must not be negative");
            }
        }
    
        public void testCheckPositionIndex_withDesc_tooHigh() {
            try {
                Preconditions.checkPositionIndex(2, 1, "foo");
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("foo (2) must not be greater than size (1)");
            }
        }
    
        public void testCheckPositionIndexes_ok() {
            Preconditions.checkPositionIndexes(0, 0, 0);
            Preconditions.checkPositionIndexes(0, 0, 1);
            Preconditions.checkPositionIndexes(0, 1, 1);
            Preconditions.checkPositionIndexes(1, 1, 1);
        }
    
        public void testCheckPositionIndexes_badSize() {
            try {
                Preconditions.checkPositionIndexes(1, 1, -1);
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testCheckPositionIndex_startNegative() {
            try {
                Preconditions.checkPositionIndexes(-1, 1, 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected).hasMessageThat().isEqualTo("start index (-1) must not be negative");
            }
        }
    
        public void testCheckPositionIndexes_endTooHigh() {
            try {
                Preconditions.checkPositionIndexes(0, 2, 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected)
                        .hasMessageThat()
                        .isEqualTo("end index (2) must not be greater than size (1)");
            }
        }
    
        public void testCheckPositionIndexes_reversed() {
            try {
                Preconditions.checkPositionIndexes(1, 0, 1);
                fail();
            } catch (IndexOutOfBoundsException expected) {
                assertThat(expected)
                        .hasMessageThat()
                        .isEqualTo("end index (0) must not be less than start index (1)");
            }
        }
    
        private static final Object IGNORE_ME =
                new Object() {
                    @Override
                    public String toString() {
                        throw new AssertionFailedError();
                    }
                };
    
        private static class Message {
            boolean invoked;
    
            @Override
            public String toString() {
                assertFalse(invoked);
                invoked = true;
                return "A message";
            }
        }
    
        private static final String FORMAT = "I ate %s pies.";
    
        private static void verifySimpleMessage(Exception e) {
            assertThat(e).hasMessageThat().isEqualTo("A message");
        }
    
        private static void verifyComplexMessage(Exception e) {
            assertThat(e).hasMessageThat().isEqualTo("I ate 5 pies.");
        }
    }

3.通用对象方法

3.1equals

当对象字段可以为null时,实现Object.equals会很麻烦,因为必须分别检查null。使用Objects.equal可以以null敏感的方式执行equals检查,没有NullPointerException的风险。

    Objects.equal("a", "a"); // returns true
    Objects.equal(null, "a"); // returns false
    Objects.equal("a", null); // returns false
    Objects.equal(null, null); // returns true

注意:JDK 7中新引入的Objects类提供了等效的Objects.equals方法。

3.2hashCode

Object对象的所有字段作散列(hash)应该更简单。Guava的Objects.hashCode(Object ...)为指定的字段序列创建一个合理的,顺序敏感的哈希。使用Objects.hashCode(field1, field2, ..., fieldn)代替手动构建哈希。

注意:JDK 7中新引入的Objects类提供了等效的Objects.hash(Object ...)

3.3toString

好的toString方法在调试中可能是无价的,但是编写起来很麻烦。使用MoreObjects.toStringHelper()轻松创建有用的toString

示例:

    // Returns "ClassName{x=1}"
    MoreObjects.toStringHelper(this)
        .add("x", 1)
        .toString();
    
    // Returns "MyObject{x=1}"
    MoreObjects.toStringHelper("MyObject")
        .add("x", 1)
        .toString();

3.4compare/compareTo

实现Comparator比较器或直接实现Comparable接口可能很麻烦。

考虑一下这种情况:

    class Person implements Comparable<Person> {
      private String lastName;
      private String firstName;
      private int zipCode;
    
      public int compareTo(Person other) {
        int cmp = lastName.compareTo(other.lastName);
        if (cmp != 0) {
          return cmp;
        }
        cmp = firstName.compareTo(other.firstName);
        if (cmp != 0) {
          return cmp;
        }
        return Integer.compare(zipCode, other.zipCode);
      }
    }

这段代码很容易弄乱,难以扫描错误,而且冗长。应该把这种代码变得更优雅,为此,Guava提供了ComparisonChain

ComparisonChain执行“惰性”比较:它仅执行比较,直到找到非零结果为止,此后它将忽略进一步的输入。

    public int compareTo(Foo that) {
        return ComparisonChain.start()
            .compare(this.aString, that.aString)
            .compare(this.anInt, that.anInt)
            .compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
            .result();
    }

这种流利的习惯用法更具可读性,不容易出现偶然的错误,并且足够聪明,不用去做必须的更多的工作。其它比较实用工具可以在Guava的“流利比较器”类Ordering中找到,在此进行解释。

3.5使用示例

    import com.google.common.base.Objects;
    import junit.framework.TestCase;
    
    public class ObjectsTest extends TestCase {
    
        public void testEqual() {
            assertTrue(Objects.equal(1, 1));
            assertTrue(Objects.equal(null, null));
    
            // test distinct string objects
            String s1 = "foobar";
            String s2 = new String(s1);
            assertTrue(Objects.equal(s1, s2));
    
            assertFalse(Objects.equal(s1, null));
            assertFalse(Objects.equal(null, s1));
            assertFalse(Objects.equal("foo", "bar"));
            assertFalse(Objects.equal("1", 1));
        }
    
        public void testHashCode() {
            int h1 = Objects.hashCode(1, "two", 3.0);
            int h2 = Objects.hashCode(new Integer(1), new String("two"), new Double(3.0));
            // repeatable
            assertEquals(h1, h2);
    
            // These don't strictly need to be true, but they're nice properties.
            assertTrue(Objects.hashCode(1, 2, null) != Objects.hashCode(1, 2));
            assertTrue(Objects.hashCode(1, 2, null) != Objects.hashCode(1, null, 2));
            assertTrue(Objects.hashCode(1, null, 2) != Objects.hashCode(1, 2));
            assertTrue(Objects.hashCode(1, 2, 3) != Objects.hashCode(3, 2, 1));
            assertTrue(Objects.hashCode(1, 2, 3) != Objects.hashCode(2, 3, 1));
        }
    
    }

4.排序

4.1示例

    assertTrue(byLengthOrdering.reverse().isOrdered(list));

4.2概述

Ordering排序是Guava的“流利”比较器Comparator类,可用于构建复杂的比较器并将其应用于对象集合。

从本质上讲,Ordering实例不过是一个特殊的Comparator实例。Ordering排序仅采用依赖于Comparator的方法(例如Collections.max),并使它们作为实例方法可用。为了增加功能,Ordering类提供了链式调用方法来调整和增强现有的比较器。

4.3创建

静态方法提供了常见的排序方式:

方法 描述
natural() 对可比较类型使用自然排序。
usingToString() 按对象的字符串表示形式按字典顺序对对象进行比较,该对象由toString()返回。

使一个预先存在的Comparator比较器成为Ordering就像使用Ordering.from(Comparator)一样简单。

但是创建自定义Ordering的更常见方法是完全跳过Comparator,而直接扩展Ordering抽象类:

    Ordering<String> byLengthOrdering = new Ordering<String>() {
      public int compare(String left, String right) {
        return Ints.compare(left.length(), right.length());
      }
    };

4.4链式调用

可以包装给定的Ordering以获得派生的排序器。一些最常用的变体包括:

方法 描述
reverse() 返回相反的排序器
nullsFirst() 返回一个Ordering在非null元素之前对null进行排序,否则其行为与原始Ordering相同。参见nullsLast()
compound(Comparator) 合成另一个比较器,以处理当前排序器中的相等情况。
lexicographical() 返回一个Ordering,根据元素按字典顺序对可迭代对象进行排序。
onResultOf(Function) 返回一个Ordering通过函数返回结果,然后使用原始Ordering比较对返回值进行排序。

例如,假设想要该类的比较器…

    class Foo {
      @Nullable String sortedBy;
      int notSortedBy;
    }

…可以处理sortedBy的空值。这是建立在链式方法之上的解决方案:

    Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(new Function<Foo, String>() {
      public String apply(Foo foo) {
        return foo.sortedBy;
      }
    });

当阅读Ordering的链式调用方法,应从右到左向后阅读。上面的示例通过查找Foo实例的sortedBy字段值对它们进行排序,首先将所有空的sortedBy值移到顶部,然后通过自然字符串排序对其余值进行排序。之所以会出现这种向后顺序,是因为每个链接调用都将先前的Ordering顺序“包装”到一个新的顺序中。

“向后”规则的例外:对于compound的调用链,请从左至右读取。为避免混淆,请避免将compound调用与其它链式调用混在一起。

链式调用超过一定长度可能很难理解。如上例所示,建议将链式调用限制为大约三个。此外,也可以通过分离中间对象(例如Function实例)来简化代码:

    Ordering<Foo> ordering = Ordering.natural().nullsFirst().onResultOf(sortKeyFunction);

4.5应用

Guava提供了多种方法使用排序来操纵或检查值或集合。在这里列出了一些最常用的:

方法 描述 另请参见
greatestOf(Iterableiterable,intk) 从最大到最小的顺序,根据此排序,返回指定可迭代的k个最大元素。不一定稳定。 leastOf
isOrdered(Iterable) 测试指定的Iterable是否按照此顺序为非递减顺序。 isStrictlyOrdered
sortedCopy(Iterable) 以List列表的形式返回指定元素的排序副本。 immutableSortedCopy
min(E,E) 根据此顺序返回其两个参数中的最小值。如果值比较相等,则返回第一个参数。 max(E,E)
min(E,E,E,E...) 根据此顺序返回其参数的最小值。如果存在多个最小值,则返回第一个。 max(E,E,E,E...)
min(Iterable) 返回指定Iterable的最小元素。如果Iterable为空,则抛出NoSuchElementException。 max(Iterable),min(Iterator),max(Iterator)

4.6使用示例

    import com.google.common.collect.ImmutableList;
    import com.google.common.collect.Lists;
    import com.google.common.collect.Ordering;
    import junit.framework.TestCase;
    import java.util.*;
    import static com.google.common.testing.SerializableTester.reserialize;
    import static com.google.common.truth.Truth.assertThat;
    import static java.util.Arrays.asList;
    
    public class OrderingTest extends TestCase {
    
        private final Ordering<Number> numberOrdering = new NumberOrdering();
    
        public void testAllEqual() {
            Ordering<Object> comparator = Ordering.allEqual();
            assertSame(comparator, comparator.reverse());
    
            assertEquals(0, comparator.compare(null, null));
            assertEquals(0, comparator.compare(new Object(), new Object()));
            assertEquals(0, comparator.compare("apples", "oranges"));
            assertSame(comparator, reserialize(comparator));
            assertEquals("Ordering.allEqual()", comparator.toString());
    
            List<String> strings = ImmutableList.of("b", "a", "d", "c");
            assertEquals(strings, comparator.sortedCopy(strings));
            assertEquals(strings, comparator.immutableSortedCopy(strings));
        }
    
        public void testFrom() {
            Ordering<String> caseInsensitiveOrdering = Ordering.from(String.CASE_INSENSITIVE_ORDER);
            assertEquals(0, caseInsensitiveOrdering.compare("A", "a"));
            assertTrue(caseInsensitiveOrdering.compare("a", "B") < 0);
            assertTrue(caseInsensitiveOrdering.compare("B", "a") > 0);
        }
    
        public void testExplicit_two() {
            Comparator<Integer> c = Ordering.explicit(42, 5);
            System.out.println(c.compare(43, 6));
            assertEquals(0, c.compare(5, 5));
            assertTrue(c.compare(5, 42) > 0);
            assertTrue(c.compare(42, 5) < 0);
        }
    
        public void testExplicit_sortingExample() {
            Comparator<Integer> c = Ordering.explicit(2, 8, 6, 1, 7, 5, 3, 4, 0, 9);
            List<Integer> list = Arrays.asList(0, 3, 5, 6, 7, 8, 9);
            Collections.sort(list, c);
            assertThat(list).containsExactly(8, 6, 7, 5, 3, 0, 9).inOrder();
        }
    
        public void testReverseOfReverseSameAsForward() {
            // Not guaranteed by spec, but it works, and saves us from testing
            // exhaustively
            assertSame(numberOrdering, numberOrdering.reverse().reverse());
        }
    
        public void testBinarySearch() {
            List<Integer> ints = Lists.newArrayList(0, 2, 3, 5, 7, 9);
            assertEquals(4, numberOrdering.binarySearch(ints, 7));
        }
    
        public void testSortedCopy() {
            List<Integer> unsortedInts = Collections.unmodifiableList(Arrays.asList(5, 0, 3, null, 0, 9));
            List<Integer> sortedInts = numberOrdering.nullsLast().sortedCopy(unsortedInts);
            assertEquals(Arrays.asList(0, 0, 3, 5, 9, null), sortedInts);
    
            assertEquals(
                    Collections.emptyList(), numberOrdering.sortedCopy(Collections.<Integer>emptyList()));
        }
    
        public void testImmutableSortedCopy() {
            ImmutableList<Integer> unsortedInts = ImmutableList.of(5, 3, 0, 9, 3);
            ImmutableList<Integer> sortedInts = numberOrdering.immutableSortedCopy(unsortedInts);
            assertEquals(Arrays.asList(0, 3, 3, 5, 9), sortedInts);
    
            assertEquals(
                    Collections.<Integer>emptyList(),
                    numberOrdering.immutableSortedCopy(Collections.<Integer>emptyList()));
    
            List<Integer> listWithNull = Arrays.asList(5, 3, null, 9);
            try {
                Ordering.natural().nullsFirst().immutableSortedCopy(listWithNull);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testIsOrdered() {
            assertFalse(numberOrdering.isOrdered(asList(5, 3, 0, 9)));
            assertFalse(numberOrdering.isOrdered(asList(0, 5, 3, 9)));
            assertTrue(numberOrdering.isOrdered(asList(0, 3, 5, 9)));
            assertTrue(numberOrdering.isOrdered(asList(0, 0, 3, 3)));
            assertTrue(numberOrdering.isOrdered(asList(0, 3)));
            assertTrue(numberOrdering.isOrdered(Collections.singleton(1)));
            assertTrue(numberOrdering.isOrdered(Collections.<Integer>emptyList()));
        }
    
        public void testIsStrictlyOrdered() {
            assertFalse(numberOrdering.isStrictlyOrdered(asList(5, 3, 0, 9)));
            assertFalse(numberOrdering.isStrictlyOrdered(asList(0, 5, 3, 9)));
            assertTrue(numberOrdering.isStrictlyOrdered(asList(0, 3, 5, 9)));
            assertFalse(numberOrdering.isStrictlyOrdered(asList(0, 0, 3, 3)));
            assertTrue(numberOrdering.isStrictlyOrdered(asList(0, 3)));
            assertTrue(numberOrdering.isStrictlyOrdered(Collections.singleton(1)));
            assertTrue(numberOrdering.isStrictlyOrdered(Collections.<Integer>emptyList()));
        }
    
        public void testLeastOfIterableLargeK() {
            List<Integer> list = Arrays.asList(4, 2, 3, 5, 1);
            assertEquals(Arrays.asList(1, 2, 3, 4, 5), Ordering.natural().leastOf(list, Integer.MAX_VALUE));
        }
    
        public void testLeastOfIteratorLargeK() {
            List<Integer> list = Arrays.asList(4, 2, 3, 5, 1);
            assertEquals(
                    Arrays.asList(1, 2, 3, 4, 5),
                    Ordering.natural().leastOf(list.iterator(), Integer.MAX_VALUE));
        }
    
        public void testGreatestOfIterable_simple() {
            /*
             * If greatestOf() promised to be implemented as reverse().leastOf(), this
             * test would be enough. It doesn't... but we'll cheat and act like it does
             * anyway. There's a comment there to remind us to fix this if we change it.
             */
            List<Integer> list = Arrays.asList(3, 1, 3, 2, 4, 2, 4, 3);
            assertEquals(Arrays.asList(4, 4, 3, 3), numberOrdering.greatestOf(list, 4));
        }
    
        public void testGreatestOfIterator_simple() {
            /*
             * If greatestOf() promised to be implemented as reverse().leastOf(), this
             * test would be enough. It doesn't... but we'll cheat and act like it does
             * anyway. There's a comment there to remind us to fix this if we change it.
             */
            List<Integer> list = Arrays.asList(3, 1, 3, 2, 4, 2, 4, 3);
            assertEquals(Arrays.asList(4, 4, 3, 3), numberOrdering.greatestOf(list.iterator(), 4));
        }
    
        public void testIteratorMinAndMax() {
            List<Integer> ints = Lists.newArrayList(5, 3, 0, 9);
            assertEquals(9, (int) numberOrdering.max(ints.iterator()));
            assertEquals(0, (int) numberOrdering.min(ints.iterator()));
    
            // when the values are the same, the first argument should be returned
            Integer a = new Integer(4);
            Integer b = new Integer(4);
            ints = Lists.newArrayList(a, b, b);
            assertSame(a, numberOrdering.max(ints.iterator()));
            assertSame(a, numberOrdering.min(ints.iterator()));
        }
    
        public void testIteratorMinExhaustsIterator() {
            List<Integer> ints = Lists.newArrayList(9, 0, 3, 5);
            Iterator<Integer> iterator = ints.iterator();
            assertEquals(0, (int) numberOrdering.min(iterator));
            assertFalse(iterator.hasNext());
        }
    
        public void testIteratorMaxExhaustsIterator() {
            List<Integer> ints = Lists.newArrayList(9, 0, 3, 5);
            Iterator<Integer> iterator = ints.iterator();
            assertEquals(9, (int) numberOrdering.max(iterator));
            assertFalse(iterator.hasNext());
        }
    
        public void testIterableMinAndMax() {
            List<Integer> ints = Lists.newArrayList(5, 3, 0, 9);
            assertEquals(9, (int) numberOrdering.max(ints));
            assertEquals(0, (int) numberOrdering.min(ints));
    
            // when the values are the same, the first argument should be returned
            Integer a = new Integer(4);
            Integer b = new Integer(4);
            ints = Lists.newArrayList(a, b, b);
            assertSame(a, numberOrdering.max(ints));
            assertSame(a, numberOrdering.min(ints));
        }
    
        public void testVarargsMinAndMax() {
            // try the min and max values in all positions, since some values are proper
            // parameters and others are from the varargs array
            assertEquals(9, (int) numberOrdering.max(9, 3, 0, 5, 8));
            assertEquals(9, (int) numberOrdering.max(5, 9, 0, 3, 8));
            assertEquals(9, (int) numberOrdering.max(5, 3, 9, 0, 8));
            assertEquals(9, (int) numberOrdering.max(5, 3, 0, 9, 8));
            assertEquals(9, (int) numberOrdering.max(5, 3, 0, 8, 9));
            assertEquals(0, (int) numberOrdering.min(0, 3, 5, 9, 8));
            assertEquals(0, (int) numberOrdering.min(5, 0, 3, 9, 8));
            assertEquals(0, (int) numberOrdering.min(5, 3, 0, 9, 8));
            assertEquals(0, (int) numberOrdering.min(5, 3, 9, 0, 8));
            assertEquals(0, (int) numberOrdering.min(5, 3, 0, 9, 0));
    
            // when the values are the same, the first argument should be returned
            Integer a = new Integer(4);
            Integer b = new Integer(4);
            assertSame(a, numberOrdering.max(a, b, b));
            assertSame(a, numberOrdering.min(a, b, b));
        }
    
        public void testParameterMinAndMax() {
            assertEquals(5, (int) numberOrdering.max(3, 5));
            assertEquals(5, (int) numberOrdering.max(5, 3));
            assertEquals(3, (int) numberOrdering.min(3, 5));
            assertEquals(3, (int) numberOrdering.min(5, 3));
    
            // when the values are the same, the first argument should be returned
            Integer a = new Integer(4);
            Integer b = new Integer(4);
            assertSame(a, numberOrdering.max(a, b));
            assertSame(a, numberOrdering.min(a, b));
        }
    
        private static class NumberOrdering extends Ordering<Number> {
            @Override
            public int compare(Number a, Number b) {
                return ((Double) a.doubleValue()).compareTo(b.doubleValue());
            }
    
            @Override
            public int hashCode() {
                return NumberOrdering.class.hashCode();
            }
    
            @Override
            public boolean equals(Object other) {
                return other instanceof NumberOrdering;
            }
    
            private static final long serialVersionUID = 0;
        }
      
    }

5.异常

Guava的Throwables 实用工具可以简化异常处理。

5.1传播

有时,当捕获异常时,想将其抛出返回到下一个try/catch块。对于RuntimeExceptionError实例,通常是这种情况,它们不需要try/catch块,但是当它们不希望被抛出时可以用try/catch块捕获。

Guava提供了几种实用程序来简化传播异常。例如:

    try {
      someMethodThatCouldThrowAnything();
    } catch (IKnowWhatToDoWithThisException e) {
      handle(e);
    } catch (Throwable t) {
      Throwables.propagateIfInstanceOf(t, IOException.class);
      Throwables.propagateIfInstanceOf(t, SQLException.class);
      throw Throwables.propagate(t);
    }

这些方法各自都会抛出异常,但是也能抛出方法返回的结果——例如,throw Throwables.propagate(t)——有助于向编译器证明将抛出异常。

以下是Guava提供的传播方法的快速摘要:

方法声明 说明
RuntimeExceptionpropagate(Throwable) 如果它是RuntimeException或Error,则按原样抛出,或者将其包装在RuntimeException中抛出。返回类型是RuntimeException,因此可以如上所述编写throwThrowables.propagate(t),Java将意识到该行肯定会抛出异常。
voidpropagateIfInstanceOf(Throwable,Class)throwsX 仅当它是X的实例时,才按原样抛出该throwable。
voidpropagateIfPossible(Throwable) 仅当它是RuntimeException或Error时才可以抛出throwable。
voidpropagateIfPossible(Throwable,Class)throwsX 仅当它是RuntimeException,Error或X时,才可以按原样抛出throwable。

5.2Throwables.propagate用法

5.2.1模拟Java 7多重捕获并重新抛出

通常,如果想让异常在堆栈中传播,则根本不需要catch块。由于你不会从异常中恢复,因此你可能不应该记录该异常或采取其他措施。你可能需要执行一些清除操作,但是通常无论操作是否成功,都需要进行清除操作,因此该操作最终会在finally块中结束。但是,重新抛出的catch块有时很有用:也许你必须在传播异常之前更新失败计数,或者可能只想有条件地传播异常。

仅处理一种异常时,捕获和重新抛出异常就很简单。当处理多种异常时,它变得凌乱:

    @Override public void run() {
      try {
        delegate.run();
      } catch (RuntimeException e) {
        failures.increment();
        throw e;
      } catch (Error e) {
        failures.increment();
        throw e;
      }
    }

Java 7通过multicatch解决了这个问题:

    } catch (RuntimeException | Error e) {
      failures.increment();
      throw e;
    }

非Java 7用户被卡住了。他们想编写如下代码,但是编译器不允许他们抛出Throwable类型的变量:

(这种写法把原本是Error或RuntimeException类型的异常修改成了Throwable,因此调用者不得不修改方法签名)

    } catch (Throwable t) {
      failures.increment();
      throw t;
    }

解决方案是用throw Throwables.propagate(t)替换throw t。在这种有限的情况下,Throwables.propagate的行为与原始代码相同。但是,使用具有其他隐藏行为的Throwables.propagate编写代码很容易。特别要注意的是,上述模式仅适用于RuntimeExceptionError。如果catch块可能捕获了受检查的异常,则还需要调用propertyIfInstanceOf以保持行为,因为Throwables.propagate无法直接传播受检查的异常。

总体而言,对传播的这种使用很一般。在Java 7中是不必要的。在其他版本下,它可以节省少量重复,但是可以简化简单的“提取方法”重构。此外,使用propagate容易意外包装检查的异常

5.2.2不必要:从"throws Throwable"转换为"throws Exception"

少数API,特别是Java反射API和(以此为基础的)JUnit,声明了抛出Throwable的方法。与这些API交互可能会很痛苦,因为即使是最通用的API通常也只能声明throws Exception。一些知道其具有非EXception异常,非Error错误的调用者使用Throwables.propagate转化Throwable。这是一个声明执行JUnit测试的Callable的示例:

    public Void call() throws Exception {
      try {
        FooTest.super.runTest();
      } catch (Throwable t) {
        Throwables.propagateIfPossible(t, Exception.class);
        Throwables.propagate(t);
      }
    
      return null;
    }

此处无需进行propagate(),因为第二行等效于throw new RuntimeException(t)。(题外话:这个示例还提醒我们,propagateIfPossible可能会引起混淆,因为它不仅传播给定类型的参数,而且传播ErrorsRuntimeExceptions。)

这种模式(或类似的变体,例如引发throw new RuntimeException(t))在Google的代码库中显示了约30次。(搜索'propagateIfPossible[^;]* Exception.class[)];')它们中的绝大多数采用显式throw new RuntimeException(t)方法。对于ThrowableException的转换,我们可能需要一个throwWrappingWeirdThrowable方法,但是考虑到两行替代,除非我们也要弃用propagateIfPossible,否则可能不需要太多。

5.2.3有争议的Throwables.propagate用途

5.2.3.1有争议的:将检查的异常转换为未检查的异常

原则上,未检查的异常表示错误,而检查的异常表示无法控制的问题。实际上,即使JDK有时也会出错ObjectIntegerURI(或者至少对于某些方法,没有答案适合每个人URI)。

结果,调用者有时不得不在异常类型之间进行转换:

    try {
      return Integer.parseInt(userInput);
    } catch (NumberFormatException e) {
      throw new InvalidInputException(e);
    }
    try {
      return publicInterfaceMethod.invoke();
    } catch (IllegalAccessException e) {
      throw new AssertionError(e);
    }

有时,这些调用者使用Throwables.propagate。缺点是什么?

最主要的是,代码的含义不太明显。Throwables.propagate(ioException)会做什么?throw new RuntimeException(ioException)有什么作用?两者的作用相同,但后者更直接。前者提出了一个问题:“这是做什么的?它不仅包装在RuntimeException中,是吗?如果是,为什么他们要编写一个方法包装器?”

诚然,这里的部分问题是“propagate”是一个模糊的名称。(这是抛出未声明异常的一种方法吗?)也许“ wrapIfChecked”会更好。即使调用了该方法,在已知的已检查异常上调用它没有任何好处。甚至还有其它缺点:也许有比普通的RuntimeException更合适的类型供抛出-例如IllegalArgumentException

有时,当仅异常可能是已检查的异常时,有时还会使用propagate。结果比替代方法小一些,直接性也小一些:

    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
    } catch (Exception e) {
      throw Throwables.propagate(e);
    }

然而,这里是将已检查的异常转换为未检查的异常的一般做法。在某些情况下,无疑这是正确的做法,但更经常地,它是用来避免处理合法的已检查异常。这使我们开始辩论有关检查异常是否总体上是一个坏主意。我不想在这里讨论所有内容。可以说,Throwables.propagate不存在为了鼓励Java用户忽略IOException等的目的。

5.2.3.2有争议的:异常隧道

但是,当要实现不允许抛出异常的方法时,该怎么办?有时,需要将异常包装在未经检查的异常中。很好,但同样,对于简单包装而言,不需要propagate。实际上,手动包装可能是更好的选择:如果包装每个异常(而不是仅检查的异常),则可以在另一端解包每个异常,从而减少特殊情况。此外,可能希望对包装使用自定义异常类型。

5.2.3.3有争议的:从其它线程重新抛出异常

    try {
      return future.get();
    } catch (ExecutionException e) {
      throw Throwables.propagate(e.getCause());
    }

这里有很多事情要考虑:

  1. 可能是经过检查的异常导致。请参阅上面的“将检查的异常转换为未检查的异常”。但是,如果已知该任务不抛出已检查异常,该怎么办?(也许这是Runnable的结果)如上所述,可以捕获异常并抛出AssertionError;propagate没什么好提供的。特别是对于Future,还要考虑Futures.get
  2. 可能是非Exception,非ErrorThrowable导致。(嗯,实际上不可能是一个,但是如果尝试直接将其重新抛出,编译器将迫使您考虑这种可能性。)请参见上面的"从throwable Throwable转换为throws Exception"。
  3. 可能是未经检查的异常或错误导致。如果是这样,它将直接重新抛出。不幸的是,它的堆栈跟踪将反映最初在其中创建异常的线程,而不是当前正在其中传播的线程。通常最好在异常链中包含两个线程的堆栈跟踪,如get抛出的ExecutionException一样。(此问题实际上与propagate无关;它与在不同线程中重新抛出异常的任何代码有关。)

5.3因果链

Guava使研究异常的因果链更为简单,它提供了三种有用的方法,其方法签名是不言自明的:

5.4使用示例

    import com.google.common.annotations.GwtIncompatible;
    import com.google.common.base.Throwables;
    import com.google.common.collect.Iterables;
    import com.google.common.primitives.Ints;
    import com.google.common.testing.NullPointerTester;
    import junit.framework.TestCase;
    import java.util.List;
    import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
    import static com.google.common.base.Throwables.*;
    import static com.google.common.truth.Truth.assertThat;
    import static java.util.Arrays.asList;
    import static java.util.regex.Pattern.quote;
    
    public class ThrowablesTest extends TestCase {
    
        public void testThrowIfUnchecked_Unchecked() {
            try {
                throwIfUnchecked(new SomeUncheckedException());
                fail();
            } catch (SomeUncheckedException expected) {
            }
        }
    
        public void testThrowIfUnchecked_Error() {
            try {
                throwIfUnchecked(new SomeError());
                fail();
            } catch (SomeError expected) {
            }
        }
    
        @SuppressWarnings("ThrowIfUncheckedKnownChecked")
        public void testThrowIfUnchecked_Checked() {
            throwIfUnchecked(new SomeCheckedException());
        }
    
        @GwtIncompatible // propagateIfPossible
        public void testPropagateIfPossible_NoneDeclared_NoneThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void noneDeclared() {
                            try {
                                methodThatDoesntThrowAnything();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(t);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect no exception to be thrown
            sample.noneDeclared();
        }
    
        @GwtIncompatible // propagateIfPossible
        public void testPropagateIfPossible_NoneDeclared_UncheckedThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void noneDeclared() {
                            try {
                                methodThatThrowsUnchecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(t);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the unchecked exception to propagate as-is
            try {
                sample.noneDeclared();
                fail();
            } catch (SomeUncheckedException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible
        public void testPropagateIfPossible_NoneDeclared_UndeclaredThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void noneDeclared() {
                            try {
                                methodThatThrowsUndeclaredChecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(t);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the undeclared exception to have been chained inside another
            try {
                sample.noneDeclared();
                fail();
            } catch (SomeChainingException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class)
        public void testPropagateIfPossible_OneDeclared_NoneThrown() throws SomeCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatDoesntThrowAnything();
                            } catch (Throwable t) {
                                // yes, this block is never reached, but for purposes of illustration
                                // we're keeping it the same in each test
                                Throwables.propagateIfPossible(t, SomeCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect no exception to be thrown
            sample.oneDeclared();
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class)
        public void testPropagateIfPossible_OneDeclared_UncheckedThrown() throws SomeCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatThrowsUnchecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(t, SomeCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the unchecked exception to propagate as-is
            try {
                sample.oneDeclared();
                fail();
            } catch (SomeUncheckedException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class)
        public void testPropagateIfPossible_OneDeclared_CheckedThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatThrowsChecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(t, SomeCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the checked exception to propagate as-is
            try {
                sample.oneDeclared();
                fail();
            } catch (SomeCheckedException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class)
        public void testPropagateIfPossible_OneDeclared_UndeclaredThrown() throws SomeCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatThrowsUndeclaredChecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(t, SomeCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the undeclared exception to have been chained inside another
            try {
                sample.oneDeclared();
                fail();
            } catch (SomeChainingException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class, Class)
        public void testPropagateIfPossible_TwoDeclared_NoneThrown()
                throws SomeCheckedException, SomeOtherCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {
                            try {
                                methodThatDoesntThrowAnything();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(
                                        t, SomeCheckedException.class, SomeOtherCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect no exception to be thrown
            sample.twoDeclared();
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class, Class)
        public void testPropagateIfPossible_TwoDeclared_UncheckedThrown()
                throws SomeCheckedException, SomeOtherCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {
                            try {
                                methodThatThrowsUnchecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(
                                        t, SomeCheckedException.class, SomeOtherCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the unchecked exception to propagate as-is
            try {
                sample.twoDeclared();
                fail();
            } catch (SomeUncheckedException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class, Class)
        public void testPropagateIfPossible_TwoDeclared_CheckedThrown() throws SomeOtherCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {
                            try {
                                methodThatThrowsChecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(
                                        t, SomeCheckedException.class, SomeOtherCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the checked exception to propagate as-is
            try {
                sample.twoDeclared();
                fail();
            } catch (SomeCheckedException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class, Class)
        public void testPropagateIfPossible_TwoDeclared_OtherCheckedThrown() throws SomeCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {
                            try {
                                methodThatThrowsOtherChecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfPossible(
                                        t, SomeCheckedException.class, SomeOtherCheckedException.class);
                                throw new SomeChainingException(t);
                            }
                        }
                    };
    
            // Expect the checked exception to propagate as-is
            try {
                sample.twoDeclared();
                fail();
            } catch (SomeOtherCheckedException expected) {
            }
        }
    
        public void testThrowIfUnchecked_null() throws SomeCheckedException {
            try {
                throwIfUnchecked(null);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        @GwtIncompatible // propagateIfPossible
        public void testPropageIfPossible_null() throws SomeCheckedException {
            Throwables.propagateIfPossible(null);
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class)
        public void testPropageIfPossible_OneDeclared_null() throws SomeCheckedException {
            Throwables.propagateIfPossible(null, SomeCheckedException.class);
        }
    
        @GwtIncompatible // propagateIfPossible(Throwable, Class, Class)
        public void testPropageIfPossible_TwoDeclared_null() throws SomeCheckedException {
            Throwables.propagateIfPossible(null, SomeCheckedException.class, SomeUncheckedException.class);
        }
    
        @GwtIncompatible // propagate
        public void testPropagate_NoneDeclared_NoneThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void noneDeclared() {
                            try {
                                methodThatDoesntThrowAnything();
                            } catch (Throwable t) {
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect no exception to be thrown
            sample.noneDeclared();
        }
    
        @GwtIncompatible // propagate
        public void testPropagate_NoneDeclared_UncheckedThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void noneDeclared() {
                            try {
                                methodThatThrowsUnchecked();
                            } catch (Throwable t) {
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect the unchecked exception to propagate as-is
            try {
                sample.noneDeclared();
                fail();
            } catch (SomeUncheckedException expected) {
            }
        }
    
        @GwtIncompatible // propagate
        public void testPropagate_NoneDeclared_ErrorThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void noneDeclared() {
                            try {
                                methodThatThrowsError();
                            } catch (Throwable t) {
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect the error to propagate as-is
            try {
                sample.noneDeclared();
                fail();
            } catch (SomeError expected) {
            }
        }
    
        @GwtIncompatible // propagate
        public void testPropagate_NoneDeclared_CheckedThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void noneDeclared() {
                            try {
                                methodThatThrowsChecked();
                            } catch (Throwable t) {
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect the undeclared exception to have been chained inside another
            try {
                sample.noneDeclared();
                fail();
            } catch (RuntimeException expected) {
                assertThat(expected).hasCauseThat().isInstanceOf(SomeCheckedException.class);
            }
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testThrowIfInstanceOf_Unchecked() throws SomeCheckedException {
            throwIfInstanceOf(new SomeUncheckedException(), SomeCheckedException.class);
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testThrowIfInstanceOf_CheckedDifferent() throws SomeCheckedException {
            throwIfInstanceOf(new SomeOtherCheckedException(), SomeCheckedException.class);
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testThrowIfInstanceOf_CheckedSame() {
            try {
                throwIfInstanceOf(new SomeCheckedException(), SomeCheckedException.class);
                fail();
            } catch (SomeCheckedException expected) {
            }
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testThrowIfInstanceOf_CheckedSubclass() {
            try {
                throwIfInstanceOf(new SomeCheckedException() {
                }, SomeCheckedException.class);
                fail();
            } catch (SomeCheckedException expected) {
            }
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testPropagateIfInstanceOf_NoneThrown() throws SomeCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatDoesntThrowAnything();
                            } catch (Throwable t) {
                                Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect no exception to be thrown
            sample.oneDeclared();
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testPropagateIfInstanceOf_DeclaredThrown() {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatThrowsChecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect declared exception to be thrown as-is
            try {
                sample.oneDeclared();
                fail();
            } catch (SomeCheckedException expected) {
            }
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testPropagateIfInstanceOf_UncheckedThrown() throws SomeCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatThrowsUnchecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect unchecked exception to be thrown as-is
            try {
                sample.oneDeclared();
                fail();
            } catch (SomeUncheckedException expected) {
            }
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testPropagateIfInstanceOf_UndeclaredThrown() throws SomeCheckedException {
            Sample sample =
                    new Sample() {
                        @Override
                        public void oneDeclared() throws SomeCheckedException {
                            try {
                                methodThatThrowsOtherChecked();
                            } catch (Throwable t) {
                                Throwables.propagateIfInstanceOf(t, SomeCheckedException.class);
                                throw Throwables.propagate(t);
                            }
                        }
                    };
    
            // Expect undeclared exception wrapped by RuntimeException to be thrown
            try {
                sample.oneDeclared();
                fail();
            } catch (RuntimeException expected) {
                assertThat(expected).hasCauseThat().isInstanceOf(SomeOtherCheckedException.class);
            }
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testThrowIfInstanceOf_null() throws SomeCheckedException {
            try {
                throwIfInstanceOf(null, SomeCheckedException.class);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        @GwtIncompatible // throwIfInstanceOf
        public void testPropageIfInstanceOf_null() throws SomeCheckedException {
            Throwables.propagateIfInstanceOf(null, SomeCheckedException.class);
        }
    
        public void testGetRootCause_NoCause() {
            SomeCheckedException exception = new SomeCheckedException();
            assertSame(exception, Throwables.getRootCause(exception));
        }
    
        public void testGetRootCause_SingleWrapped() {
            SomeCheckedException cause = new SomeCheckedException();
            SomeChainingException exception = new SomeChainingException(cause);
            assertSame(cause, Throwables.getRootCause(exception));
        }
    
        public void testGetRootCause_DoubleWrapped() {
            SomeCheckedException cause = new SomeCheckedException();
            SomeChainingException exception = new SomeChainingException(new SomeChainingException(cause));
            assertSame(cause, Throwables.getRootCause(exception));
        }
    
        public void testGetRootCause_Loop() {
            Exception cause = new Exception();
            Exception exception = new Exception(cause);
            cause.initCause(exception);
            try {
                Throwables.getRootCause(cause);
                fail("Should have throw IAE");
            } catch (IllegalArgumentException expected) {
                assertThat(expected).hasCauseThat().isSameInstanceAs(cause);
            }
        }
    
        private static class SomeError extends Error {
        }
    
        private static class SomeCheckedException extends Exception {
        }
    
        private static class SomeOtherCheckedException extends Exception {
        }
    
        private static class SomeUncheckedException extends RuntimeException {
        }
    
        private static class SomeUndeclaredCheckedException extends Exception {
        }
    
        private static class SomeChainingException extends RuntimeException {
            public SomeChainingException(Throwable cause) {
                super(cause);
            }
        }
    
        static class Sample {
            void noneDeclared() {
            }
    
            void oneDeclared() throws SomeCheckedException {
            }
    
            void twoDeclared() throws SomeCheckedException, SomeOtherCheckedException {
            }
        }
    
        static void methodThatDoesntThrowAnything() {
        }
    
        static void methodThatThrowsError() {
            throw new SomeError();
        }
    
        static void methodThatThrowsUnchecked() {
            throw new SomeUncheckedException();
        }
    
        static void methodThatThrowsChecked() throws SomeCheckedException {
            throw new SomeCheckedException();
        }
    
        static void methodThatThrowsOtherChecked() throws SomeOtherCheckedException {
            throw new SomeOtherCheckedException();
        }
    
        static void methodThatThrowsUndeclaredChecked() throws SomeUndeclaredCheckedException {
            throw new SomeUndeclaredCheckedException();
        }
    
        @GwtIncompatible // getStackTraceAsString(Throwable)
        public void testGetStackTraceAsString() {
            class StackTraceException extends Exception {
                StackTraceException(String message) {
                    super(message);
                }
            }
    
            StackTraceException e = new StackTraceException("my message");
    
            String firstLine = quote(e.getClass().getName() + ": " + e.getMessage());
            String secondLine = "\\s*at " + ThrowablesTest.class.getName() + "\\..*";
            String moreLines = "(?:.*\n?)*";
            String expected = firstLine + "\n" + secondLine + "\n" + moreLines;
            assertThat(getStackTraceAsString(e)).matches(expected);
        }
    
        public void testGetCausalChain() {
            SomeUncheckedException sue = new SomeUncheckedException();
            IllegalArgumentException iae = new IllegalArgumentException(sue);
            RuntimeException re = new RuntimeException(iae);
            IllegalStateException ex = new IllegalStateException(re);
    
            assertEquals(asList(ex, re, iae, sue), Throwables.getCausalChain(ex));
            assertSame(sue, Iterables.getOnlyElement(Throwables.getCausalChain(sue)));
    
            List<Throwable> causes = Throwables.getCausalChain(ex);
            try {
                causes.add(new RuntimeException());
                fail("List should be unmodifiable");
            } catch (UnsupportedOperationException expected) {
            }
        }
    
        public void testGetCasualChainNull() {
            try {
                Throwables.getCausalChain(null);
                fail("Should have throw NPE");
            } catch (NullPointerException expected) {
            }
        }
    
        public void testGetCasualChainLoop() {
            Exception cause = new Exception();
            Exception exception = new Exception(cause);
            cause.initCause(exception);
            try {
                Throwables.getCausalChain(cause);
                fail("Should have throw IAE");
            } catch (IllegalArgumentException expected) {
                assertThat(expected).hasCauseThat().isSameInstanceAs(cause);
            }
        }
    
        @GwtIncompatible // Throwables.getCauseAs(Throwable, Class)
        public void testGetCauseAs() {
            SomeCheckedException cause = new SomeCheckedException();
            SomeChainingException thrown = new SomeChainingException(cause);
    
            assertThat(thrown).hasCauseThat().isSameInstanceAs(cause);
            assertThat(Throwables.getCauseAs(thrown, SomeCheckedException.class)).isSameInstanceAs(cause);
            assertThat(Throwables.getCauseAs(thrown, Exception.class)).isSameInstanceAs(cause);
    
            try {
                Throwables.getCauseAs(thrown, IllegalStateException.class);
                fail("Should have thrown CCE");
            } catch (ClassCastException expected) {
                assertThat(expected).hasCauseThat().isSameInstanceAs(thrown);
            }
        }
    
        @GwtIncompatible // lazyStackTraceIsLazy()
        public void testLazyStackTraceWorksInProd() {
            // TODO(b/64442212): Remove this guard once lazyStackTrace() works in Java 9+.
            Integer javaVersion = Ints.tryParse(JAVA_SPECIFICATION_VERSION.value());
            if (javaVersion != null && javaVersion >= 9) {
                return;
            }
            // Obviously this isn't guaranteed in every environment, but it works well enough for now:
            assertTrue(lazyStackTraceIsLazy());
        }
    
        @GwtIncompatible // lazyStackTrace(Throwable)
        public void testLazyStackTrace() {
            Exception e = new Exception();
            StackTraceElement[] originalStackTrace = e.getStackTrace();
    
            assertThat(lazyStackTrace(e)).containsExactly((Object[]) originalStackTrace).inOrder();
    
            try {
                lazyStackTrace(e).set(0, null);
                fail();
            } catch (UnsupportedOperationException expected) {
            }
    
            // Now we test a property that holds only for the lazy implementation.
    
            if (!lazyStackTraceIsLazy()) {
                return;
            }
    
            e.setStackTrace(new StackTraceElement[0]);
            assertThat(lazyStackTrace(e)).containsExactly((Object[]) originalStackTrace).inOrder();
        }
    
        @GwtIncompatible // lazyStackTrace
        private void doTestLazyStackTraceFallback() {
            assertFalse(lazyStackTraceIsLazy());
    
            Exception e = new Exception();
    
            assertThat(lazyStackTrace(e)).containsExactly((Object[]) e.getStackTrace()).inOrder();
    
            try {
                lazyStackTrace(e).set(0, null);
                fail();
            } catch (UnsupportedOperationException expected) {
            }
    
            e.setStackTrace(new StackTraceElement[0]);
            assertThat(lazyStackTrace(e)).isEmpty();
        }
    
        @GwtIncompatible // NullPointerTester
        public void testNullPointers() {
            new NullPointerTester().testAllPublicStaticMethods(Throwables.class);
        }
    }

本文参考:
Google Guava wiki
Using and avoiding null
Preconditions
Common object methods
Ordering
Throwables
guava-tests-base
guava-tests-collect


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文