8021591: Additional explicit null checks

Reviewed-by: psandoz, alanb
This commit is contained in:
Mike Duigou 2013-09-13 11:18:44 -07:00
parent 767ab8c9ae
commit b59dc6762e
16 changed files with 604 additions and 310 deletions

@ -3900,6 +3900,7 @@ public class Collections {
return batchRemove(c, true);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<Map.Entry<K,V>> it = iterator();
while (it.hasNext()) {

@ -957,6 +957,8 @@ public class Hashtable<K,V>
@Override
public synchronized boolean replace(K key, V oldValue, V newValue) {
Objects.requireNonNull(oldValue);
Objects.requireNonNull(newValue);
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@ -977,6 +979,7 @@ public class Hashtable<K,V>
@Override
public synchronized V replace(K key, V value) {
Objects.requireNonNull(value);
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;

@ -997,6 +997,7 @@ public class IdentityHashMap<K,V>
* behavior when c is a smaller "normal" (non-identity-based) Set.
*/
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
for (Iterator<K> i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {
@ -1212,6 +1213,7 @@ public class IdentityHashMap<K,V>
* behavior when c is a smaller "normal" (non-identity-based) Set.
*/
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); ) {
if (c.contains(i.next())) {

@ -805,6 +805,10 @@ public interface Map<K,V> {
* return false;
* }</pre>
*
* The default implementation does not throw NullPointerException
* for maps that do not support null values if oldValue is null unless
* newValue is also null.
*
* @param key key with which the specified value is associated
* @param oldValue value expected to be associated with the specified key
* @param newValue value to be associated with the specified key
@ -814,8 +818,11 @@ public interface Map<K,V> {
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws ClassCastException if the class of a specified key or value
* prevents it from being stored in this map
* @throws NullPointerException if a specified key or value is null,
* @throws NullPointerException if a specified key or newValue is null,
* and this map does not permit null keys or values
* @throws NullPointerException if oldValue is null and this map does not
* permit null values
* (<a href="Collection.html#optional-restrictions">optional</a>)
* @throws IllegalArgumentException if some property of a specified key
* or value prevents it from being stored in this map
* @since 1.8

@ -1012,7 +1012,7 @@ public class TreeMap<K,V>
int expectedModCount = modCount;
for (Entry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
e.value = Objects.requireNonNull(function.apply(e.key, e.value));
e.value = function.apply(e.key, e.value);
if (expectedModCount != modCount) {
throw new ConcurrentModificationException();

@ -49,6 +49,7 @@ import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.Spliterator;
import java.util.concurrent.ConcurrentMap;
@ -4410,6 +4411,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
}
public final boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
for (Iterator<E> it = iterator(); it.hasNext();) {
if (c.contains(it.next())) {
@ -4421,6 +4423,7 @@ public class ConcurrentHashMap<K,V> extends AbstractMap<K,V>
}
public final boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
for (Iterator<E> it = iterator(); it.hasNext();) {
if (!c.contains(it.next())) {

@ -1186,7 +1186,7 @@ public final class Subject implements java.io.Serializable {
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
final Iterator<E> e = iterator();
while (e.hasNext()) {
@ -1222,7 +1222,7 @@ public final class Subject implements java.io.Serializable {
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
boolean retain = false;
final Iterator<E> e = iterator();

@ -21,15 +21,19 @@
* questions.
*/
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@ -38,43 +42,68 @@ import static org.testng.Assert.fail;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* @test
* @library testlibrary
* @build CollectionAsserts CollectionSupplier
* @run testng CollectionDefaults
* @summary Unit tests for extension methods on Collection
* @library testlibrary
* @build CollectionAsserts CollectionSupplier ExtendsAbstractSet ExtendsAbstractCollection
* @run testng CollectionDefaults
*/
public class CollectionDefaults {
public static final Predicate<Integer> pEven = x -> 0 == x % 2;
public static final Predicate<Integer> pOdd = x -> 1 == x % 2;
private static final String[] SET_CLASSES = {
"java.util.HashSet",
"java.util.LinkedHashSet",
"java.util.TreeSet"
@SuppressWarnings("unchecked")
private static final Supplier<?>[] TEST_CLASSES = {
// Collection
ExtendsAbstractCollection<Integer>::new,
// Lists
java.util.ArrayList<Integer>::new,
java.util.LinkedList<Integer>::new,
java.util.Vector<Integer>::new,
java.util.concurrent.CopyOnWriteArrayList<Integer>::new,
ExtendsAbstractList<Integer>::new,
// Sets
java.util.HashSet<Integer>::new,
java.util.LinkedHashSet<Integer>::new,
java.util.TreeSet<Integer>::new,
java.util.concurrent.ConcurrentSkipListSet<Integer>::new,
java.util.concurrent.CopyOnWriteArraySet<Integer>::new,
ExtendsAbstractSet<Integer>::new
};
private static final int SIZE = 100;
@DataProvider(name="setProvider", parallel=true)
public static Object[][] setCases() {
public static Iterator<Object[]> setCases() {
final List<Object[]> cases = new LinkedList<>();
cases.add(new Object[] { new HashSet<>() });
cases.add(new Object[] { new LinkedHashSet<>() });
cases.add(new Object[] { new TreeSet<>() });
cases.add(new Object[] { new java.util.concurrent.ConcurrentSkipListSet<>() });
cases.add(new Object[] { new java.util.concurrent.CopyOnWriteArraySet<>() });
cases.add(new Object[] { new ExtendsAbstractSet<>() });
cases.add(new Object[] { Collections.newSetFromMap(new HashMap<>()) });
cases.add(new Object[] { Collections.newSetFromMap(new LinkedHashMap()) });
cases.add(new Object[] { Collections.newSetFromMap(new TreeMap<>()) });
cases.add(new Object[] { Collections.newSetFromMap(new ConcurrentHashMap<>()) });
cases.add(new Object[] { Collections.newSetFromMap(new ConcurrentSkipListMap<>()) });
cases.add(new Object[] { new HashSet(){{add(42);}} });
cases.add(new Object[] { new LinkedHashSet(){{add(42);}} });
cases.add(new Object[] { new TreeSet(){{add(42);}} });
return cases.toArray(new Object[0][cases.size()]);
cases.add(new Object[] { new HashSet<Integer>(){{add(42);}} });
cases.add(new Object[] { new ExtendsAbstractSet<Integer>(){{add(42);}} });
cases.add(new Object[] { new LinkedHashSet<Integer>(){{add(42);}} });
cases.add(new Object[] { new TreeSet<Integer>(){{add(42);}} });
return cases.iterator();
}
@Test(dataProvider = "setProvider")
@ -82,57 +111,66 @@ public class CollectionDefaults {
try {
set.forEach(null);
fail("expected NPE not thrown");
} catch (NullPointerException npe) {}
} catch (NullPointerException expected) {
; // expected
}
try {
set.removeIf(null);
fail("expected NPE not thrown");
} catch (NullPointerException npe) {}
} catch (NullPointerException expected) {
; // expected
}
}
@Test
public void testForEach() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final Set<Integer> original = ((Set<Integer>) test.original);
final Set<Integer> set = ((Set<Integer>) test.collection);
final CollectionSupplier<Collection<Integer>> supplier = new CollectionSupplier((Supplier<Collection<Integer>>[]) TEST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<Collection<Integer>> test : supplier.get()) {
final Collection<Integer> original = test.expected;
final Collection<Integer> set = test.collection;
try {
set.forEach(null);
fail("expected NPE not thrown");
} catch (NullPointerException npe) {}
if (test.className.equals("java.util.HashSet")) {
CollectionAsserts.assertContentsUnordered(set, original);
} catch (NullPointerException expected) {
; // expected
}
if (set instanceof Set && !((set instanceof SortedSet) || (set instanceof LinkedHashSet))) {
CollectionAsserts.assertContentsUnordered(set, original, test.toString());
} else {
CollectionAsserts.assertContents(set, original);
CollectionAsserts.assertContents(set, original, test.toString());
}
final List<Integer> actual = new LinkedList<>();
set.forEach(actual::add);
if (test.className.equals("java.util.HashSet")) {
CollectionAsserts.assertContentsUnordered(actual, set);
CollectionAsserts.assertContentsUnordered(actual, original);
if (set instanceof Set && !((set instanceof SortedSet) || (set instanceof LinkedHashSet))) {
CollectionAsserts.assertContentsUnordered(actual, set, test.toString());
CollectionAsserts.assertContentsUnordered(actual, original, test.toString());
} else {
CollectionAsserts.assertContents(actual, set);
CollectionAsserts.assertContents(actual, original);
CollectionAsserts.assertContents(actual, set, test.toString());
CollectionAsserts.assertContents(actual, original, test.toString());
}
}
}
@Test
public void testRemoveIf() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(SET_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final Set<Integer> original = ((Set<Integer>) test.original);
final Set<Integer> set = ((Set<Integer>) test.collection);
final CollectionSupplier<Collection<Integer>> supplier = new CollectionSupplier((Supplier<Collection<Integer>>[]) TEST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<Collection<Integer>> test : supplier.get()) {
final Collection<Integer> original = test.expected;
final Collection<Integer> set = test.collection;
try {
set.removeIf(null);
fail("expected NPE not thrown");
} catch (NullPointerException npe) {}
if (test.className.equals("java.util.HashSet")) {
CollectionAsserts.assertContentsUnordered(set, original);
} catch (NullPointerException expected) {
; // expected
}
if (set instanceof Set && !((set instanceof SortedSet) || (set instanceof LinkedHashSet))) {
CollectionAsserts.assertContentsUnordered(set, original, test.toString());
} else {
CollectionAsserts.assertContents(set, original);
CollectionAsserts.assertContents(set, original, test.toString());
}
set.removeIf(pEven);

@ -400,8 +400,6 @@ public class MOAT {
// If add(null) succeeds, contains(null) & remove(null) should succeed
//----------------------------------------------------------------
private static void testNullElement(Collection<Integer> c) {
// !!!! 5018849: (coll) TreeSet.contains(null) does not agree with Javadoc
if (c instanceof TreeSet) return;
try {
check(c.add(null));

@ -41,6 +41,10 @@ import static org.testng.Assert.fail;
*/
public class CollectionAsserts {
private CollectionAsserts() {
// no instances
}
public static void assertCountSum(Iterable<? super Integer> it, int count, int sum) {
assertCountSum(it.iterator(), count, sum);
}
@ -117,10 +121,18 @@ public class CollectionAsserts {
}
public static<T> void assertContents(Iterable<T> actual, Iterable<T> expected) {
assertContents(actual.iterator(), expected.iterator());
assertContents(actual, expected, null);
}
public static<T> void assertContents(Iterable<T> actual, Iterable<T> expected, String msg) {
assertContents(actual.iterator(), expected.iterator(), msg);
}
public static<T> void assertContents(Iterator<T> actual, Iterator<T> expected) {
assertContents(actual, expected, null);
}
public static<T> void assertContents(Iterator<T> actual, Iterator<T> expected, String msg) {
List<T> history = new ArrayList<>();
while (expected.hasNext()) {
@ -128,20 +140,23 @@ public class CollectionAsserts {
List<T> expectedData = new ArrayList<>(history);
while (expected.hasNext())
expectedData.add(expected.next());
fail(String.format("Premature end of data; expected=%s, found=%s", expectedData, history));
fail(String.format("%s Premature end of data; expected=%s, found=%s",
(msg == null ? "" : msg), expectedData, history));
}
T a = actual.next();
T e = expected.next();
history.add(a);
if (!Objects.equals(a, e))
fail(String.format("Data mismatch; preceding=%s, nextExpected=%s, nextFound=%s", history, e, a));
fail(String.format("%s Data mismatch; preceding=%s, nextExpected=%s, nextFound=%s",
(msg == null ? "" : msg), history, e, a));
}
if (actual.hasNext()) {
List<T> rest = new ArrayList<>();
while (actual.hasNext())
rest.add(actual.next());
fail(String.format("Unexpected data %s after %s", rest, history));
fail(String.format("%s Unexpected data %s after %s",
(msg == null ? "" : msg), rest, history));
}
}
@ -151,30 +166,21 @@ public class CollectionAsserts {
assertContents(actual, Arrays.asList(expected).iterator());
}
public static <T> boolean equalsContentsUnordered(Iterable<T> a, Iterable<T> b) {
Set<T> sa = new HashSet<>();
for (T t : a) {
sa.add(t);
}
Set<T> sb = new HashSet<>();
for (T t : b) {
sb.add(t);
}
return Objects.equals(sa, sb);
public static<T extends Comparable<? super T>> void assertContentsUnordered(Iterable<T> actual, Iterable<T> expected) {
assertContentsUnordered(actual, expected, null);
}
public static<T extends Comparable<? super T>> void assertContentsUnordered(Iterable<T> actual, Iterable<T> expected) {
ArrayList<T> one = new ArrayList<>();
for (T t : actual)
one.add(t);
ArrayList<T> two = new ArrayList<>();
for (T t : expected)
two.add(t);
Collections.sort(one);
Collections.sort(two);
assertContents(one, two);
public static<T extends Comparable<? super T>> void assertContentsUnordered(Iterable<T> actual, Iterable<T> expected, String msg) {
List<T> allExpected = new ArrayList<>();
for (T t : expected) {
allExpected.add(t);
}
for (T t : actual) {
assertTrue(allExpected.remove(t), msg + " element '" + String.valueOf(t) + "' not found");
}
assertTrue(allExpected.isEmpty(), msg + "expected contained additional elements");
}
static <T> void assertSplitContents(Iterable<Iterable<T>> splits, Iterable<T> list) {

@ -29,13 +29,11 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.testng.TestException;
import static org.testng.Assert.assertTrue;
import java.lang.reflect.Constructor;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Supplier;
@ -44,73 +42,61 @@ import java.util.function.Supplier;
* @library
* @summary A Supplier of test cases for Collection tests
*/
public final class CollectionSupplier implements Supplier<Iterable<CollectionSupplier.TestCase>> {
public final class CollectionSupplier<C extends Collection<Integer>> implements Supplier<Iterable<CollectionSupplier.TestCase<C>>> {
private final String[] classNames;
private final Supplier<C>[] classes;
private final int size;
/**
* A Collection test case.
*/
public static final class TestCase {
public static final class TestCase<C extends Collection<Integer>> {
/**
* The name of the test case.
*/
public final String name;
/**
* Class name of the instantiated Collection.
*/
public final String className;
/**
* Unmodifiable reference collection, useful for comparisons.
*/
public final Collection<Integer> original;
public final List<Integer> expected;
/**
* A modifiable test collection.
*/
public final Collection<Integer> collection;
public final C collection;
/**
* Create a Collection test case.
*
* @param name name of the test case
* @param className class name of the instantiated collection
* @param original reference collection
* @param expected reference collection
* @param collection the modifiable test collection
*/
public TestCase(String name, String className,
Collection<Integer> original, Collection<Integer> collection) {
public TestCase(String name, C collection) {
this.name = name;
this.className = className;
this.original =
List.class.isAssignableFrom(original.getClass()) ?
Collections.unmodifiableList((List<Integer>) original) :
Set.class.isAssignableFrom(original.getClass()) ?
Collections.unmodifiableSet((Set<Integer>) original) :
Collections.unmodifiableCollection(original);
this.expected = Collections.unmodifiableList(
Arrays.asList(collection.toArray(new Integer[0])));
this.collection = collection;
}
@Override
public String toString() {
return name + " " + className +
"\n original: " + original +
"\n target: " + collection;
return name + " " + collection.getClass().toString();
}
}
/**
* Shuffle a list using a PRNG with known seed for repeatability
*
* @param list the list to be shuffled
*/
public static <E> void shuffle(final List<E> list) {
// PRNG with known seed for repeatable tests
final Random prng = new Random(13);
final int size = list.size();
for (int i=0; i < size; i++) {
for (int i = 0; i < size; i++) {
// random index in interval [i, size)
final int j = i + prng.nextInt(size - i);
// swap elements at indices i & j
@ -127,178 +113,133 @@ public final class CollectionSupplier implements Supplier<Iterable<CollectionSup
* @param classNames class names that implement {@code Collection}
* @param size the desired size of each collection
*/
public CollectionSupplier(String[] classNames, int size) {
this.classNames = Arrays.copyOf(classNames, classNames.length);
public CollectionSupplier(Supplier<C>[] classes, int size) {
this.classes = Arrays.copyOf(classes, classes.length);
this.size = size;
}
@Override
public Iterable<TestCase> get() {
try {
return getThrows();
} catch (Exception e) {
throw new TestException(e);
}
}
public Iterable<TestCase<C>> get() {
final Collection<TestCase<C>> cases = new LinkedList<>();
for (final Supplier<C> type : classes) {
try {
final Collection<Integer> empty = type.get();
cases.add(new TestCase("empty", empty));
private Iterable<TestCase> getThrows() throws Exception {
final Collection<TestCase> collections = new LinkedList<>();
for (final String className : classNames) {
@SuppressWarnings("unchecked")
final Class<? extends Collection<Integer>> type =
(Class<? extends Collection<Integer>>) Class.forName(className);
final Constructor<? extends Collection<Integer>>
defaultConstructor = type.getConstructor();
final Constructor<? extends Collection<Integer>>
copyConstructor = type.getConstructor(Collection.class);
final Collection<Integer> single = type.get();
single.add(42);
cases.add(new TestCase("single", single));
final Collection<Integer> empty = defaultConstructor.newInstance();
collections.add(new TestCase("empty",
className,
copyConstructor.newInstance(empty),
empty));
final Collection<Integer> single = defaultConstructor.newInstance();
single.add(42);
collections.add(new TestCase("single",
className,
copyConstructor.newInstance(single),
single));
final Collection<Integer> regular = defaultConstructor.newInstance();
for (int i=0; i < size; i++) {
regular.add(i);
}
collections.add(new TestCase("regular",
className,
copyConstructor.newInstance(regular),
regular));
final Collection<Integer> reverse = defaultConstructor.newInstance();
for (int i=size; i >= 0; i--) {
reverse.add(i);
}
collections.add(new TestCase("reverse",
className,
copyConstructor.newInstance(reverse),
reverse));
final Collection<Integer> odds = defaultConstructor.newInstance();
for (int i=0; i < size; i++) {
odds.add((i * 2) + 1);
}
collections.add(new TestCase("odds",
className,
copyConstructor.newInstance(odds),
odds));
final Collection<Integer> evens = defaultConstructor.newInstance();
for (int i=0; i < size; i++) {
evens.add(i * 2);
}
collections.add(new TestCase("evens",
className,
copyConstructor.newInstance(evens),
evens));
final Collection<Integer> fibonacci = defaultConstructor.newInstance();
int prev2 = 0;
int prev1 = 1;
for (int i=0; i < size; i++) {
final int n = prev1 + prev2;
if (n < 0) { // stop on overflow
break;
final Collection<Integer> regular = type.get();
for (int i = 0; i < size; i++) {
regular.add(i);
}
fibonacci.add(n);
prev2 = prev1;
prev1 = n;
}
collections.add(new TestCase("fibonacci",
className,
copyConstructor.newInstance(fibonacci),
fibonacci));
cases.add(new TestCase("regular", regular));
final Collection<Integer> reverse = type.get();
for (int i = size; i >= 0; i--) {
reverse.add(i);
}
cases.add(new TestCase("reverse", reverse));
final Collection<Integer> odds = type.get();
for (int i = 0; i < size; i++) {
odds.add((i * 2) + 1);
}
cases.add(new TestCase("odds", odds));
final Collection<Integer> evens = type.get();
for (int i = 0; i < size; i++) {
evens.add(i * 2);
}
cases.add(new TestCase("evens", evens));
final Collection<Integer> fibonacci = type.get();
int prev2 = 0;
int prev1 = 1;
for (int i = 0; i < size; i++) {
final int n = prev1 + prev2;
if (n < 0) { // stop on overflow
break;
}
fibonacci.add(n);
prev2 = prev1;
prev1 = n;
}
cases.add(new TestCase("fibonacci", fibonacci));
// variants where the size of the backing storage != reported size
// created by removing half of the elements
// created by removing half of the elements
final Collection<Integer> emptyWithSlack = type.get();
emptyWithSlack.add(42);
assertTrue(emptyWithSlack.remove(42));
cases.add(new TestCase("emptyWithSlack", emptyWithSlack));
final Collection<Integer> emptyWithSlack = defaultConstructor.newInstance();
emptyWithSlack.add(42);
assertTrue(emptyWithSlack.remove(42));
collections.add(new TestCase("emptyWithSlack",
className,
copyConstructor.newInstance(emptyWithSlack),
emptyWithSlack));
final Collection<Integer> singleWithSlack = type.get();
singleWithSlack.add(42);
singleWithSlack.add(43);
assertTrue(singleWithSlack.remove(43));
cases.add(new TestCase("singleWithSlack", singleWithSlack));
final Collection<Integer> singleWithSlack = defaultConstructor.newInstance();
singleWithSlack.add(42);
singleWithSlack.add(43);
assertTrue(singleWithSlack.remove(43));
collections.add(new TestCase("singleWithSlack",
className,
copyConstructor.newInstance(singleWithSlack),
singleWithSlack));
final Collection<Integer> regularWithSlack = defaultConstructor.newInstance();
for (int i=0; i < (2 * size); i++) {
regularWithSlack.add(i);
}
assertTrue(regularWithSlack.removeIf((x) -> {return x >= size;}));
collections.add(new TestCase("regularWithSlack",
className,
copyConstructor.newInstance(regularWithSlack),
regularWithSlack));
final Collection<Integer> reverseWithSlack = defaultConstructor.newInstance();
for (int i=2 * size; i >= 0; i--) {
reverseWithSlack.add(i);
}
assertTrue(reverseWithSlack.removeIf((x) -> {return x < size;}));
collections.add(new TestCase("reverseWithSlack",
className,
copyConstructor.newInstance(reverseWithSlack),
reverseWithSlack));
final Collection<Integer> oddsWithSlack = defaultConstructor.newInstance();
for (int i = 0; i < 2 * size; i++) {
oddsWithSlack.add((i * 2) + 1);
}
assertTrue(oddsWithSlack.removeIf((x) -> {return x >= size;}));
collections.add(new TestCase("oddsWithSlack",
className,
copyConstructor.newInstance(oddsWithSlack),
oddsWithSlack));
final Collection<Integer> evensWithSlack = defaultConstructor.newInstance();
for (int i = 0; i < 2 * size; i++) {
evensWithSlack.add(i * 2);
}
assertTrue(evensWithSlack.removeIf((x) -> {return x >= size;}));
collections.add(new TestCase("evensWithSlack",
className,
copyConstructor.newInstance(evensWithSlack),
evensWithSlack));
final Collection<Integer> fibonacciWithSlack = defaultConstructor.newInstance();
prev2 = 0;
prev1 = 1;
for (int i=0; i < size; i++) {
final int n = prev1 + prev2;
if (n < 0) { // stop on overflow
break;
final Collection<Integer> regularWithSlack = type.get();
for (int i = 0; i < (2 * size); i++) {
regularWithSlack.add(i);
}
fibonacciWithSlack.add(n);
prev2 = prev1;
prev1 = n;
}
assertTrue(fibonacciWithSlack.removeIf((x) -> {return x < 20;}));
collections.add(new TestCase("fibonacciWithSlack",
className,
copyConstructor.newInstance(fibonacciWithSlack),
fibonacciWithSlack));
assertTrue(regularWithSlack.removeIf((x) -> {
return x >= size;
}));
cases.add(new TestCase("regularWithSlack", regularWithSlack));
final Collection<Integer> reverseWithSlack = type.get();
for (int i = 2 * size; i >= 0; i--) {
reverseWithSlack.add(i);
}
assertTrue(reverseWithSlack.removeIf((x) -> {
return x < size;
}));
cases.add(new TestCase("reverseWithSlack", reverseWithSlack));
final Collection<Integer> oddsWithSlack = type.get();
for (int i = 0; i < 2 * size; i++) {
oddsWithSlack.add((i * 2) + 1);
}
assertTrue(oddsWithSlack.removeIf((x) -> {
return x >= size;
}));
cases.add(new TestCase("oddsWithSlack", oddsWithSlack));
final Collection<Integer> evensWithSlack = type.get();
for (int i = 0; i < 2 * size; i++) {
evensWithSlack.add(i * 2);
}
assertTrue(evensWithSlack.removeIf((x) -> {
return x >= size;
}));
cases.add(new TestCase("evensWithSlack", evensWithSlack));
final Collection<Integer> fibonacciWithSlack = type.get();
prev2 = 0;
prev1 = 1;
for (int i = 0; i < size; i++) {
final int n = prev1 + prev2;
if (n < 0) { // stop on overflow
break;
}
fibonacciWithSlack.add(n);
prev2 = prev1;
prev1 = n;
}
assertTrue(fibonacciWithSlack.removeIf((x) -> {
return x < 20;
}));
cases.add(new TestCase("fibonacciWithSlack",
fibonacciWithSlack));
} catch (Exception failed) {
throw new TestException(failed);
}
}
return collections;
return cases;
}
}

@ -0,0 +1,86 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.AbstractCollection;
import java.util.HashSet;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.function.Supplier;
/**
* @library
*
* A simple mutable collection implementation that provides only default
* implementations of all methods. ie. none of the Collection interface default
* methods have overridden implementations.
*
* @param <E> type of collection elements
*/
public class ExtendsAbstractCollection<E> extends AbstractCollection<E> {
protected final Collection<E> coll;
public ExtendsAbstractCollection() {
this(ArrayList<E>::new);
}
public ExtendsAbstractCollection(Collection<E> source) {
this();
coll.addAll(source);
}
protected ExtendsAbstractCollection(Supplier<Collection<E>> backer) {
this.coll = backer.get();
}
public boolean add(E element) {
return coll.add(element);
}
public boolean remove(Object element) {
return coll.remove(element);
}
public Iterator<E> iterator() {
return new Iterator<E>() {
Iterator<E> source = coll.iterator();
public boolean hasNext() {
return source.hasNext();
}
public E next() {
return source.next();
}
public void remove() {
source.remove();
}
};
}
public int size() {
return coll.size();
}
}

@ -0,0 +1,101 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.ArrayList;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
/**
* @library
*
* A simple mutable list implementation that provides only default
* implementations of all methods. ie. none of the List interface default
* methods have overridden implementations.
*
* @param <E> type of list elements
*/
public class ExtendsAbstractList<E> extends AbstractList<E> {
protected final List<E> list;
public ExtendsAbstractList() {
this(ArrayList<E>::new);
}
protected ExtendsAbstractList(Supplier<List<E>> supplier) {
this.list = supplier.get();
}
public ExtendsAbstractList(Collection<E> source) {
this();
addAll(source);
}
public boolean add(E element) {
return list.add(element);
}
public E get(int index) {
return list.get(index);
}
public boolean remove(Object element) {
return list.remove(element);
}
public E set(int index, E element) {
return list.set(index, element);
}
public void add(int index, E element) {
list.add(index, element);
}
public E remove(int index) {
return list.remove(index);
}
public Iterator<E> iterator() {
return new Iterator<E>() {
Iterator<E> source = list.iterator();
public boolean hasNext() {
return source.hasNext();
}
public E next() {
return source.next();
}
public void remove() {
source.remove();
}
};
}
public int size() {
return list.size();
}
}

@ -0,0 +1,85 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.util.HashSet;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.function.Supplier;
/**
* @library
*
* A simple mutable set implementation that provides only default
* implementations of all methods. ie. none of the Set interface default methods
* have overridden implementations.
*
* @param <E> type of set members
*/
public class ExtendsAbstractSet<E> extends AbstractSet<E> {
protected final Set<E> set;
public ExtendsAbstractSet() {
this(HashSet<E>::new);
}
public ExtendsAbstractSet(Collection<E> source) {
this();
addAll(source);
}
protected ExtendsAbstractSet(Supplier<Set<E>> backer) {
this.set = backer.get();
}
public boolean add(E element) {
return set.add(element);
}
public boolean remove(Object element) {
return set.remove(element);
}
public Iterator<E> iterator() {
return new Iterator<E>() {
Iterator<E> source = set.iterator();
public boolean hasNext() {
return source.hasNext();
}
public E next() {
return source.next();
}
public void remove() {
source.remove();
}
};
}
public int size() {
return set.size();
}
}

@ -28,8 +28,6 @@ import java.util.Comparator;
import java.util.List;
import java.util.LinkedList;
import java.util.Stack;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.Vector;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@ -46,28 +44,30 @@ import static org.testng.Assert.fail;
import java.lang.reflect.Constructor;
import java.util.ConcurrentModificationException;
import java.util.function.Predicate;
import java.util.function.Supplier;
/**
* @test
* @bug 8023367
* @library testlibrary
* @build CollectionAsserts CollectionSupplier
* @run testng ListDefaults
* @summary Unit tests for extension methods on List
* @bug 8023367
* @library ../Collection/testlibrary
* @build CollectionAsserts CollectionSupplier ExtendsAbstractList
* @run testng ListDefaults
*/
public class ListDefaults {
private static final String[] LIST_CLASSES = {
"java.util.ArrayList",
"java.util.LinkedList",
"java.util.Vector",
"java.util.concurrent.CopyOnWriteArrayList"
};
private static final Supplier<?>[] LIST_CLASSES = {
java.util.ArrayList::new,
java.util.LinkedList::new,
java.util.Vector::new,
java.util.concurrent.CopyOnWriteArrayList::new,
ExtendsAbstractList::new
};
private static final String[] LIST_CME_CLASSES = {
"java.util.ArrayList",
"java.util.Vector"
};
private static final Supplier<?>[] LIST_CME_CLASSES = {
java.util.ArrayList::new,
java.util.Vector::new
};
private static final Predicate<Integer> pEven = x -> 0 == x % 2;
private static final Predicate<Integer> pOdd = x -> 1 == x % 2;
@ -139,13 +139,9 @@ public class ListDefaults {
@Test
public void testForEach() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.original);
final List<Integer> list = ((List<Integer>) test.collection);
}
for (final CollectionSupplier.TestCase test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.original);
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.expected);
final List<Integer> list = ((List<Integer>) test.collection);
try {
@ -182,10 +178,9 @@ public class ListDefaults {
@Test
public void testRemoveIf() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.original);
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.expected);
final List<Integer> list = ((List<Integer>) test.collection);
try {
@ -201,7 +196,7 @@ public class ListDefaults {
}
for (final CollectionSupplier.TestCase test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.original);
final List<Integer> original = ((List<Integer>) test.expected);
final List<Integer> list = ((List<Integer>) test.collection);
list.removeIf(pOdd);
for (int i : list) {
@ -217,7 +212,7 @@ public class ListDefaults {
}
for (final CollectionSupplier.TestCase test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.original);
final List<Integer> original = ((List<Integer>) test.expected);
final List<Integer> list = ((List<Integer>) test.collection);
final List<Integer> listCopy = new ArrayList<>(list);
if (original.size() > SUBLIST_SIZE) {
@ -274,9 +269,9 @@ public class ListDefaults {
@Test
public void testReplaceAll() throws Exception {
final int scale = 3;
final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.original);
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.expected);
final List<Integer> list = ((List<Integer>) test.collection);
try {
@ -329,9 +324,9 @@ public class ListDefaults {
@Test
public void testSort() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.original);
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.expected);
final List<Integer> list = ((List<Integer>) test.collection);
CollectionSupplier.shuffle(list);
list.sort(Integer::compare);
@ -378,17 +373,15 @@ public class ListDefaults {
}
@SuppressWarnings("unchecked")
final Class<? extends List<AtomicInteger>> type =
(Class<? extends List<AtomicInteger>>) Class.forName(test.className);
final Constructor<? extends List<AtomicInteger>> defaultConstructor = type.getConstructor();
final Constructor<? extends List<?>> defaultConstructor = ((Class<? extends List<?>>)test.collection.getClass()).getConstructor();
final List<AtomicInteger> incomparables = (List<AtomicInteger>) defaultConstructor.newInstance();
for (int i=0; i < test.original.size(); i++) {
for (int i=0; i < test.expected.size(); i++) {
incomparables.add(new AtomicInteger(i));
}
CollectionSupplier.shuffle(incomparables);
incomparables.sort(ATOMIC_INTEGER_COMPARATOR);
for (int i=0; i < test.original.size(); i++) {
for (int i=0; i < test.expected.size(); i++) {
assertEquals(i, incomparables.get(i).intValue());
}
@ -427,9 +420,10 @@ public class ListDefaults {
@Test
public void testForEachThrowsCME() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> list = ((List<Integer>) test.collection);
if (list.size() <= 1) {
continue;
}
@ -448,9 +442,11 @@ public class ListDefaults {
@Test
public void testRemoveIfThrowsCME() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> original = ((List<Integer>) test.expected);
final List<Integer> list = ((List<Integer>) test.collection);
if (list.size() <= 1) {
continue;
}
@ -469,9 +465,10 @@ public class ListDefaults {
@Test
public void testReplaceAllThrowsCME() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> list = ((List<Integer>) test.collection);
if (list.size() <= 1) {
continue;
}
@ -490,9 +487,10 @@ public class ListDefaults {
@Test
public void testSortThrowsCME() throws Exception {
final CollectionSupplier supplier = new CollectionSupplier(LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase test : supplier.get()) {
final CollectionSupplier<List<Integer>> supplier = new CollectionSupplier((Supplier<List<Integer>>[])LIST_CME_CLASSES, SIZE);
for (final CollectionSupplier.TestCase<List<Integer>> test : supplier.get()) {
final List<Integer> list = ((List<Integer>) test.collection);
if (list.size() <= 1) {
continue;
}
@ -520,6 +518,7 @@ public class ListDefaults {
cases.add(new Object[] { new LinkedList<>(Arrays.asList(DATA)) });
cases.add(new Object[] { new Vector<>(Arrays.asList(DATA)) });
cases.add(new Object[] { new CopyOnWriteArrayList<>(Arrays.asList(DATA)) });
cases.add(new Object[] { new ExtendsAbstractList<>(Arrays.asList(DATA)) });
return cases.toArray(new Object[0][cases.size()]);
}

@ -155,7 +155,7 @@ public class Defaults {
assertThrows(
() -> { map.replaceAll((k,v) -> null); },
NullPointerException.class,
description);
description + " should not allow replacement with null value");
}
@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=withNull values=withNull")
@ -194,6 +194,15 @@ public class Defaults {
assertSame(map.get(null), EXTRA_VALUE);
}
@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
public void testReplaceKVNoNulls(String description, Map<IntegerEnum, String> map) {
assertTrue(map.containsKey(FIRST_KEY), "expected key missing");
assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");
assertThrows( () -> {map.replace(FIRST_KEY, null);}, NullPointerException.class, description + ": should throw NPE");
assertSame(map.replace(FIRST_KEY, EXTRA_VALUE), FIRST_VALUE, description + ": replaced wrong value");
assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");
}
@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
public void testReplaceKV(String description, Map<IntegerEnum, String> map) {
assertTrue(map.containsKey(KEYS[1]));
@ -224,6 +233,16 @@ public class Defaults {
assertSame(map.get(null), EXTRA_VALUE);
}
@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=nonNull values=nonNull")
public void testReplaceKVVNoNulls(String description, Map<IntegerEnum, String> map) {
assertTrue(map.containsKey(FIRST_KEY), "expected key missing");
assertSame(map.get(FIRST_KEY), FIRST_VALUE, "found wrong value");
assertThrows( () -> {map.replace(FIRST_KEY, FIRST_VALUE, null);}, NullPointerException.class, description + ": should throw NPE");
assertThrows( () -> {if (!map.replace(FIRST_KEY, null, EXTRA_VALUE)) throw new NullPointerException("default returns false rather than throwing");}, NullPointerException.class, description + ": should throw NPE");
assertTrue(map.replace(FIRST_KEY, FIRST_VALUE, EXTRA_VALUE), description + ": replaced wrong value");
assertSame(map.get(FIRST_KEY), EXTRA_VALUE, "found wrong value");
}
@Test(dataProvider = "Map<IntegerEnum,String> rw=true keys=all values=all")
public void testReplaceKVV(String description, Map<IntegerEnum, String> map) {
assertTrue(map.containsKey(KEYS[1]));
@ -470,6 +489,9 @@ public class Defaults {
VALUES[each] = String.valueOf(each);
}
}
private static final IntegerEnum FIRST_KEY = KEYS[0];
private static final String FIRST_VALUE = VALUES[0];
private static final IntegerEnum EXTRA_KEY = IntegerEnum.EXTRA_KEY;
private static final String EXTRA_VALUE = String.valueOf(TEST_SIZE);
@ -583,6 +605,8 @@ public class Defaults {
return Arrays.asList(
// null key hostile
new Object[]{"EnumMap", makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls)},
new Object[]{"TreeMap", makeMap(TreeMap::new, false, nulls)},
new Object[]{"ExtendsAbstractMap(TreeMap)", makeMap(() -> {return new ExtendsAbstractMap(new TreeMap());}, false, nulls)},
new Object[]{"Collections.synchronizedMap(EnumMap)", Collections.synchronizedMap(makeMap(() -> new EnumMap(IntegerEnum.class), false, nulls))}
);
}
@ -591,10 +615,11 @@ public class Defaults {
return Arrays.asList(
// null key and value hostile
new Object[]{"Hashtable", makeMap(Hashtable::new, false, false)},
new Object[]{"TreeMap", makeMap(TreeMap::new, false, false)},
new Object[]{"ConcurrentHashMap", makeMap(ConcurrentHashMap::new, false, false)},
new Object[]{"ConcurrentSkipListMap", makeMap(ConcurrentSkipListMap::new, false, false)},
new Object[]{"Collections.synchronizedMap(ConcurrentHashMap)", Collections.synchronizedMap(makeMap(ConcurrentHashMap::new, false, false))},
new Object[]{"Collections.checkedMap(ConcurrentHashMap)", Collections.checkedMap(makeMap(ConcurrentHashMap::new, false, false), IntegerEnum.class, String.class)},
new Object[]{"ExtendsAbstractMap(ConcurrentHashMap)", makeMap(() -> {return new ExtendsAbstractMap(new ConcurrentHashMap());}, false, false)},
new Object[]{"ImplementsConcurrentMap", makeMap(ImplementsConcurrentMap::new, false, false)}
);
}
@ -641,18 +666,17 @@ public class Defaults {
}
public static <T extends Throwable> void assertThrows(Thrower<T> thrower, Class<T> throwable, String message) {
Throwable result;
Throwable thrown;
try {
thrower.run();
result = null;
thrown = null;
} catch (Throwable caught) {
result = caught;
thrown = caught;
}
assertInstance(result, throwable,
(null != message)
? message
: "Failed to throw " + throwable.getCanonicalName());
assertInstance(thrown, throwable,
((null != message) ? message : "") +
" Failed to throw " + throwable.getCanonicalName());
}
public static <T extends Throwable> void assertThrows(Class<T> throwable, String message, Thrower<T>... throwers) {
@ -661,11 +685,11 @@ public class Defaults {
}
}
public static <T> void assertInstance(T actual, Class<? extends T> expected) {
public static void assertInstance(Object actual, Class<?> expected) {
assertInstance(expected.isInstance(actual), null);
}
public static <T> void assertInstance(T actual, Class<? extends T> expected, String message) {
public static void assertInstance(Object actual, Class<?> expected, String message) {
assertTrue(expected.isInstance(actual), message);
}