| /* |
| * Copyright (C) 2016 The Guava Authors |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.common.reflect; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import java.io.Serializable; |
| import java.util.Comparator; |
| import java.util.List; |
| import junit.framework.TestCase; |
| |
| @AndroidIncompatible // lots of failures, possibly some related to bad equals() implementations? |
| public class TypeTokenSubtypeTest extends TestCase { |
| |
| public void testOwnerTypeSubtypes() throws Exception { |
| new OwnerTypeSubtypingTests().testAllDeclarations(); |
| } |
| |
| public void testWildcardSubtypes() throws Exception { |
| new WildcardSubtypingTests().testAllDeclarations(); |
| } |
| |
| /** |
| * This test reproduces the bug in canonicalizeWildcardType() when the type variable is |
| * recursively bounded. |
| */ |
| public void testRecursiveWildcardSubtypeBug() throws Exception { |
| try { |
| new RecursiveTypeBoundBugExample<>().testAllDeclarations(); |
| fail(); |
| } catch (Exception e) { |
| assertThat(e).hasCauseThat().isInstanceOf(AssertionError.class); |
| } |
| } |
| |
| public void testSubtypeOfInnerClass_nonStaticAnonymousClass() { |
| TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {}; |
| Class<?> subclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass(); |
| assertTrue(TypeToken.of(subclass).isSubtypeOf(supertype)); |
| } |
| |
| public void testSubtypeOfInnerClass_nonStaticAnonymousClass_typeParameterOfOwnerTypeNotMatch() { |
| TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {}; |
| Class<?> subclass = new Mall<Indoor>().new Shop<Electronics>() {}.getClass(); |
| assertFalse(TypeToken.of(subclass).isSubtypeOf(supertype)); |
| } |
| |
| public void testSubtypeOfInnerClass_nonStaticAnonymousClass_typeParameterOfInnerTypeNotMatch() { |
| TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {}; |
| Class<?> subclass = new Mall<Outdoor>().new Shop<Grocery>() {}.getClass(); |
| assertFalse(TypeToken.of(subclass).isSubtypeOf(supertype)); |
| } |
| |
| public static void testSubtypeOfInnerClass_staticAnonymousClass() { |
| TypeToken<?> supertype = new TypeToken<Mall<Outdoor>.Shop<Electronics>>() {}; |
| Class<?> subclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass(); |
| assertTrue(TypeToken.of(subclass).isSubtypeOf(supertype)); |
| } |
| |
| public static void testSubtypeOfStaticAnonymousClass() { |
| Class<?> superclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass(); |
| assertTrue(TypeToken.of(superclass).isSubtypeOf(superclass)); |
| assertFalse( |
| TypeToken.of(new Mall<Outdoor>().new Shop<Electronics>() {}.getClass()) |
| .isSubtypeOf(superclass)); |
| } |
| |
| public void testSubtypeOfNonStaticAnonymousClass() { |
| Class<?> superclass = new Mall<Outdoor>().new Shop<Electronics>() {}.getClass(); |
| assertTrue(TypeToken.of(superclass).isSubtypeOf(superclass)); |
| assertFalse( |
| TypeToken.of(new Mall<Outdoor>().new Shop<Electronics>() {}.getClass()) |
| .isSubtypeOf(superclass)); |
| } |
| |
| public void testGetSubtypeOf_impossibleWildcard() { |
| TypeToken<List<? extends Number>> numberList = new TypeToken<List<? extends Number>>() {}; |
| abstract class StringList implements List<String> {} |
| try { |
| numberList.getSubtype(StringList.class); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| private static class OwnerTypeSubtypingTests extends SubtypeTester { |
| @TestSubtype |
| public Mall<Outdoor>.Shop<Electronics> innerTypeIsSubtype( |
| Mall<Outdoor>.Retailer<Electronics> retailer) { |
| return isSubtype(retailer); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Mall<Outdoor>.Shop<? extends Electronics> innerTypeIsSubtype_supertypeWithWildcard( |
| Mall<Outdoor>.Retailer<Electronics> retailer) { |
| return isSubtype(retailer); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Mall<?>.Shop<?> innerTypeIsSubtype_withWildcards( |
| Mall<? extends Indoor>.Retailer<? extends Electronics> retailer) { |
| return isSubtype(retailer); |
| } |
| |
| @TestSubtype |
| public Mall<Outdoor>.Shop<Electronics> ownerTypeIsSubtype( |
| Outlet<Outdoor>.Shop<Electronics> shop) { |
| return isSubtype(shop); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Mall<? extends Outdoor>.Shop<Electronics> ownerTypeIsSubtype_supertypeWithWildcard( |
| Outlet<Outdoor>.Shop<Electronics> shop) { |
| return isSubtype(shop); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Mall<?>.Shop<Electronics> ownerTypeIsSubtype_withWildcards( |
| Outlet<? extends Outdoor>.Shop<Electronics> shop) { |
| return isSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Mall<Outdoor>.Shop<Electronics> bothOwnerTypeAndInnerTypeAreSubtypes( |
| Outlet<Outdoor>.Retailer<Electronics> retailer) { |
| return isSubtype(retailer); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Mall<? super Outdoor>.Shop<? extends Electronics> |
| bothOwnerTypeAndInnerTypeAreSubtypes_supertypeWithWildcard( |
| Outlet<Outdoor>.Retailer<Electronics> retailer) { |
| return isSubtype(retailer); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Mall<? super Outdoor>.Shop<? extends Electronics> |
| bothOwnerTypeAndInnerTypeAreSubtypes_withWildcards( |
| Outlet<Outdoor>.Retailer<Electronics> retailer) { |
| return isSubtype(retailer); |
| } |
| |
| @TestSubtype |
| public Mall<Outdoor>.Shop<Electronics> ownerTypeDoesNotMatch( |
| Mall<Indoor>.Shop<Electronics> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Mall<Outdoor>.Shop<Electronics> ownerTypeDoesNotMatch_subtypeWithWildcard( |
| Mall<? extends Outdoor>.Shop<Electronics> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Mall<? extends Outdoor>.Shop<Electronics> ownerTypeDoesNotMatch_supertypeWithWildcard( |
| Mall<?>.Shop<Electronics> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Mall<Outdoor>.Retailer<Electronics> innerTypeDoesNotMatch( |
| Mall<Outdoor>.Shop<Grocery> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Mall<Outdoor>.Shop<Electronics> innerTypeDoesNotMatch_subtypeWithWildcard( |
| Mall<Outdoor>.Shop<? extends Electronics> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Mall<Outdoor>.Shop<? extends Electronics> innerTypeDoesNotMatch_supertypeWithWildcard( |
| Mall<Outdoor>.Shop<Grocery> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype(suppressGetSubtype = true) |
| public ConsumerFacing<Electronics> supertypeIsNestedClass( |
| Mall<Indoor>.Retailer<Electronics> shop) { |
| return isSubtype(shop); |
| } |
| |
| @TestSubtype |
| public ConsumerFacing<Grocery> nestedClassIsNotSupertypeDueToTypeParameter( |
| Mall<Indoor>.Shop<Electronics> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype |
| public ConsumerFacing<Grocery> nestedClassIsNotSupertype(Mall<Indoor>.Shop<Grocery> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype(suppressGetSubtype = true) |
| public Comparator<Electronics> supertypeIsTopLevelClass( |
| Mall<Indoor>.Retailer<Electronics> shop) { |
| return isSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Comparator<Electronics> topLevelClassIsNotSupertypeDueToTypeParameter( |
| Mall<Indoor>.Retailer<Grocery> shop) { |
| return notSubtype(shop); |
| } |
| |
| @TestSubtype |
| public Comparator<Electronics> topLevelClassIsNotSupertype( |
| Mall<Indoor>.Shop<Electronics> shop) { |
| return notSubtype(shop); |
| } |
| } |
| |
| private static class RecursiveTypeBoundBugExample<T extends RecursiveTypeBoundBugExample<T>> |
| extends SubtypeTester { |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public List<RecursiveTypeBoundBugExample<?>> ifYouUseTheTypeVariableOnTheClassAndItIsRecursive( |
| List<RecursiveTypeBoundBugExample<? extends RecursiveTypeBoundBugExample<T>>> arg) { |
| return notSubtype(arg); // isSubtype() currently incorectly considers it a subtype. |
| } |
| } |
| |
| private static class WildcardSubtypingTests extends SubtypeTester { |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<?> noBounds(List<?> list) { |
| return isSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<List<?>> listOfListOfWildcard(List<List<?>> listOfList) { |
| return isSubtype(listOfList); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<? extends List<?>> listOfWildcardListOfWildcard( |
| List<? extends List<?>> listOfList) { |
| return isSubtype(listOfList); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Enum<? extends Enum<?>> implicitlyBoundedEnumIsSubtypeOfExplicitlyBoundedEnum( |
| Enum<?> e) { |
| return isSubtype(e); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Enum<?> implicitlyBoundedEnum(Enum<?> e) { |
| return isSubtype(e); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Enum<?> explicitlyBoundedEnumIsSubtypeOfImplicitlyBoundedEnum( |
| Enum<? extends Enum<?>> obj) { |
| return isSubtype(obj); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<Enum<?>> listOfEnums(List<Enum<?>> listOfEnums) { |
| return isSubtype(listOfEnums); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseList<? extends List<Enum<? extends Enum<?>>>> |
| wildcardBoundUsesImplicitlyRecursiveBoundedWildcard(UseList<? extends List<Enum<?>>> arg) { |
| return isSubtype(arg); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseList<? extends List<Enum<? extends Enum<?>>>> |
| wildcardBoundHasImplicitBoundAtsInvariantPosition(UseList<? extends List<Enum<?>>> arg) { |
| return isSubtype(arg); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<Enum<?>> listOfEnumsWithExplicitBoundIsSubtypeOfIterableOfEnumWithImplicitBound( |
| List<Enum<? extends Enum<?>>> listOfEnums) { |
| return isSubtype(listOfEnums); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<Enum<?>> nestedExplicitEnumBoundIsSubtypeOfImplicitEnumBound( |
| List<Enum<? extends Enum<? extends Enum<?>>>> listOfEnums) { |
| return isSubtype(listOfEnums); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<Enum<? extends Enum<? extends Enum<?>>>> |
| implicitEnumBoundIsSubtypeOfNestedExplicitEnumBound(List<Enum<?>> listOfEnums) { |
| return isSubtype(listOfEnums); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<Enum<? extends Enum<?>>> |
| listOfEnumsWithImplicitBoundIsSubtypeOfIterableOfEnumWithExplicitBound( |
| List<Enum<?>> listOfEnums) { |
| return isSubtype(listOfEnums); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public List<Enum<? extends Enum<?>>> listOfSubEnumsIsNotSubtypeOfListOfEnums( |
| List<MyEnum> listOfEnums) { |
| return notSubtype(listOfEnums); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public List<MyTypeBoundUsesImplicitBound<? extends Enum<?>>> typeVariableBoundOmitsItsOwnBound( |
| List<MyTypeBoundUsesImplicitBound<?>> arg) { |
| return isSubtype(arg); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public List<MyTypeBoundUsesImplicitBound<? extends MyEnum>> |
| wildcardUpperBoundIsNotSubtypeOfTypeVariableBound( |
| List<MyTypeBoundUsesImplicitBound<?>> arg) { |
| return notSubtype(arg); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public List<List<? extends Iterable<UseList<? extends List<?>>>>> |
| wildcardBoundUsesParameterizedTypeWithImplicitBound( |
| List<List<? extends Iterable<UseList<?>>>> arg) { |
| return isSubtype(arg); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public List<List<? extends Iterable<Enum<? extends Enum<?>>>>> |
| wildcardBoundUsesRecursiveParameterizedTypeWithImplicitBound( |
| List<List<? extends Iterable<Enum<?>>>> arg) { |
| return isSubtype(arg); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public List<List<? extends Iterable<MyTypeBoundUsesImplicitBound<? extends Enum<?>>>>> |
| wildcardBoundUsesParameterizedTypeDefinedWithImplicitBound( |
| List<List<? extends Iterable<MyTypeBoundUsesImplicitBound<?>>>> arg) { |
| return isSubtype(arg); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<UseIterable<? extends Iterable<?>>> |
| wildcardOfImplicitBoundedIsSubtypeOfWildcardOfExplicitlyBounded( |
| List<UseIterable<?>> withImplicitBounds) { |
| return isSubtype(withImplicitBounds); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<UseSerializableIterable<? extends Iterable<?>>> |
| wildcardOfImplicitBoundedIsSubtypeOfWildcardOfExplicitlyPartialBounded( |
| List<UseSerializableIterable<?>> withImplicitBounds) { |
| return isSubtype(withImplicitBounds); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<UseList<? extends Iterable<?>>> useListOfIterableWildcard( |
| List<UseList<?>> withImplicitBounds) { |
| return isSubtype(withImplicitBounds); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<UseIterable<?>> listOfExplicitBoundedIsSubtypeOfListOfImplicitlyBounded( |
| List<UseIterable<? extends Iterable<?>>> withExplicitBounds) { |
| return isSubtype(withExplicitBounds); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<UseIterable<? extends Iterable<?>>> |
| wildcardOfImplicitBoundedIsNotSubtypeOfNonWildcardOfExplicitlyBounded( |
| List<? extends UseIterable<?>> withImplicitBounds) { |
| return notSubtype(withImplicitBounds); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public Iterable<UseIterable<? extends List<?>>> |
| wildcardOfImplicitBoundedIsNotSubtypeOfWildcardWithNarrowerBounds( |
| List<UseIterable<?>> withImplicitBounds) { |
| return notSubtype(withImplicitBounds); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<? extends T> supertypeWithWildcardUpperBound(List<T> list) { |
| return isSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<? extends T> supertypeWithWildcardUpperBound_notMatch(List<String> list) { |
| return notSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<? super T> supertypeWithWildcardULowerBound(List<T> list) { |
| return isSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<? extends T> supertypeWithWildcardULowerBound_notMatch(List<String> list) { |
| return notSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<?> wildcardsMatchByUpperBound(List<? extends T> list) { |
| return isSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<? extends T> wildCardsDoNotMatchByUpperBound(List<?> list) { |
| return notSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<? super String> wildcardsMatchByLowerBound( |
| List<? super CharSequence> list) { |
| return isSubtype(list); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public <T> Iterable<? super CharSequence> wildCardsDoNotMatchByLowerBound( |
| List<? super String> list) { |
| return notSubtype(list); |
| } |
| |
| // Can't test getSupertype() or getSubtype() because JDK reflection doesn't consider |
| // Foo<?> and Foo<? extends Bar> equal for class Foo<T extends Bar> |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseIterable<?> explicitTypeBoundIsSubtypeOfImplicitTypeBound( |
| UseIterable<? extends Iterable<?>> obj) { |
| return isSubtype(obj); |
| } |
| |
| // Can't test getSupertype() or getSubtype() because JDK reflection doesn't consider |
| // Foo<?> and Foo<? extends Bar> equal for class Foo<T extends Bar> |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseIterable<? extends Iterable<?>> implicitTypeBoundIsSubtypeOfExplicitTypeBound( |
| UseIterable<?> obj) { |
| return isSubtype(obj); |
| } |
| |
| // Can't test getSupertype() or getSubtype() because JDK reflection doesn't consider |
| // Foo<?> and Foo<? extends Bar> equal for class Foo<T extends Bar> |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseIterable<? extends Iterable<?>> omittedTypeBoundIsSubtypeOfExplicitTypeBound( |
| UseIterable<? extends CharSequence> obj) { |
| return isSubtype(obj); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseSerializableIterable<? extends Serializable> |
| implicitTypeBoundIsSubtypeOfPartialExplicitTypeBound(UseSerializableIterable<?> obj) { |
| return isSubtype(obj); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseSerializableIterable<?> partialImplicitTypeBoundIsSubtypeOfImplicitTypeBound( |
| UseSerializableIterable<? extends Iterable<?>> obj) { |
| return isSubtype(obj); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseSerializableIterable<? extends CharSequence> |
| implicitTypeBoundIsNotSubtypeOfDifferentTypeBound(UseSerializableIterable<?> obj) { |
| return notSubtype(obj); |
| } |
| |
| @TestSubtype(suppressGetSupertype = true, suppressGetSubtype = true) |
| public UseSerializableIterable<? extends CharSequence> |
| partialExplicitTypeBoundIsNotSubtypeOfDifferentTypeBound( |
| UseSerializableIterable<? extends Serializable> obj) { |
| return notSubtype(obj); |
| } |
| } |
| |
| // TODO(benyu): migrate all subtyping tests from TypeTokenTest to this class using SubtypeTester. |
| |
| private interface Outdoor {} |
| |
| private interface Indoor {} |
| |
| private interface Grocery {} |
| |
| private interface Electronics {} |
| |
| private interface ConsumerFacing<T> {} |
| |
| private static class Mall<T> { |
| class Shop<ProductT> {} |
| |
| abstract class Retailer<ProductT> extends Shop<ProductT> |
| implements Comparator<ProductT>, ConsumerFacing<ProductT> {} |
| } |
| |
| private static class Outlet<T> extends Mall<T> {} |
| |
| private interface UseIterable<T extends Iterable<?>> {} |
| |
| private interface UseSerializableIterable<T extends Serializable & Iterable<?>> {} |
| |
| private interface UseList<T extends List<?>> {} |
| |
| private enum MyEnum {} |
| |
| private interface MyTypeBoundUsesImplicitBound<E extends Enum<?>> {} |
| } |