blob: 3ae040f049c7c03367308caab1a0660460c8efb4 [file] [log] [blame]
crazyboblee41bc8522006-12-08 07:06:55 +00001/**
2 * Copyright (C) 2006 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.google.inject;
18
19import static com.google.inject.util.Objects.nonNull;
crazyboblee41bc8522006-12-08 07:06:55 +000020import java.lang.reflect.ParameterizedType;
kevinb9na99dca72007-02-11 04:48:57 +000021import java.lang.reflect.Type;
crazyboblee41bc8522006-12-08 07:06:55 +000022
23/**
crazyboblee235d0682007-01-31 02:25:21 +000024 * Represents a generic type {@code T}. Java doesn't yet provide a way to
crazybobleee3adfd62007-02-02 21:30:08 +000025 * represent generic types, so this class does. Forces clients to create a
kevinb9na99dca72007-02-11 04:48:57 +000026 * subclass of this class which enables retrieval the type information even at
27 * runtime.
crazybobleee3adfd62007-02-02 21:30:08 +000028 *
kevinb9na99dca72007-02-11 04:48:57 +000029 * <p>For example, to create a type literal for {@code List<String>}, you can
30 * create an empty anonymous inner class:
crazybobleee3adfd62007-02-02 21:30:08 +000031 *
kevinb9na99dca72007-02-11 04:48:57 +000032 * <p>
33 * {@code TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {};}
crazyboblee41bc8522006-12-08 07:06:55 +000034 *
kevinb9na99dca72007-02-11 04:48:57 +000035 * <p>Assumes that type {@code T} implements {@link Object#equals} and
36 * {@link Object#hashCode()} as value (as opposed to identity) comparison.
crazyboblee41bc8522006-12-08 07:06:55 +000037 *
38 * @author crazybob@google.com (Bob Lee)
39 */
crazyboblee0baa9fc2007-01-31 02:38:54 +000040public abstract class TypeLiteral<T> {
crazyboblee41bc8522006-12-08 07:06:55 +000041
42 final Class<? super T> rawType;
43 final Type type;
44
45 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +000046 * Constructs a new type literal. Derives represented class from type
crazyboblee41bc8522006-12-08 07:06:55 +000047 * parameter.
48 *
49 * <p>Clients create an empty anonymous subclass. Doing so embeds the type
kevinb9na99dca72007-02-11 04:48:57 +000050 * parameter in the anonymous class's type hierarchy so we can reconstitute it
51 * at runtime despite erasure.
crazyboblee41bc8522006-12-08 07:06:55 +000052 */
kevinb9na99dca72007-02-11 04:48:57 +000053 @SuppressWarnings("unchecked")
crazyboblee0baa9fc2007-01-31 02:38:54 +000054 protected TypeLiteral() {
crazyboblee41bc8522006-12-08 07:06:55 +000055 this.type = getSuperclassTypeParameter(getClass());
56 this.rawType = (Class<? super T>) getRawType(type);
57 }
58
59 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +000060 * Unsafe. Constructs a type literal manually.
crazyboblee41bc8522006-12-08 07:06:55 +000061 */
kevinb9na99dca72007-02-11 04:48:57 +000062 @SuppressWarnings("unchecked")
crazyboblee0baa9fc2007-01-31 02:38:54 +000063 private TypeLiteral(Type type) {
crazyboblee41bc8522006-12-08 07:06:55 +000064 this.rawType = (Class<? super T>) getRawType(nonNull(type, "type"));
65 this.type = type;
66 }
67
68 /**
69 * Gets type from super class's type parameter.
70 */
71 static Type getSuperclassTypeParameter(Class<?> subclass) {
72 Type superclass = subclass.getGenericSuperclass();
73 if (superclass instanceof Class) {
74 throw new RuntimeException("Missing type parameter.");
75 }
76 return ((ParameterizedType) superclass).getActualTypeArguments()[0];
77 }
78
79 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +000080 * Gets type literal from super class's type parameter.
crazyboblee41bc8522006-12-08 07:06:55 +000081 */
crazyboblee0baa9fc2007-01-31 02:38:54 +000082 static TypeLiteral<?> fromSuperclassTypeParameter(Class<?> subclass) {
83 return new SimpleTypeLiteral<Object>(getSuperclassTypeParameter(subclass));
crazyboblee41bc8522006-12-08 07:06:55 +000084 }
85
kevinb9na99dca72007-02-11 04:48:57 +000086 @SuppressWarnings({ "unchecked" })
crazyboblee41bc8522006-12-08 07:06:55 +000087 private static Class<?> getRawType(Type type) {
88 if (type instanceof Class<?>) {
89 // type is a normal class.
90 return (Class<?>) type;
kevinb9na99dca72007-02-11 04:48:57 +000091 }
92 else {
crazyboblee41bc8522006-12-08 07:06:55 +000093 // type is a parameterized type.
94 if (!(type instanceof ParameterizedType)) {
95 unexpectedType(type, ParameterizedType.class);
96 }
97 ParameterizedType parameterizedType = (ParameterizedType) type;
98
99 // I'm not exactly sure why getRawType() returns Type instead of Class.
100 // Neal isn't either but suspects some pathological case related
101 // to nested classes exists.
102 Type rawType = parameterizedType.getRawType();
103 if (!(rawType instanceof Class<?>)) {
104 unexpectedType(rawType, Class.class);
105 }
106 return (Class<?>) rawType;
107 }
108 }
109
110 /**
111 * Gets the raw type.
112 */
113 Class<? super T> getRawType() {
114 return rawType;
115 }
116
117 /**
118 * Gets underlying {@code Type} instance.
119 */
120 public Type getType() {
121 return type;
122 }
123
124 public int hashCode() {
125 return type.hashCode();
126 }
127
128 public boolean equals(Object o) {
129 if (o == this) {
130 return true;
131 }
crazyboblee0baa9fc2007-01-31 02:38:54 +0000132 if (!(o instanceof TypeLiteral<?>)) {
crazyboblee41bc8522006-12-08 07:06:55 +0000133 return false;
134 }
crazyboblee0baa9fc2007-01-31 02:38:54 +0000135 TypeLiteral<?> t = (TypeLiteral<?>) o;
crazyboblee41bc8522006-12-08 07:06:55 +0000136 return type.equals(t.type);
137 }
138
139 public String toString() {
140 return type instanceof Class<?>
kevinb9na99dca72007-02-11 04:48:57 +0000141 ? ((Class<?>) type).getName()
142 : type.toString();
crazyboblee41bc8522006-12-08 07:06:55 +0000143 }
144
crazyboblee0baa9fc2007-01-31 02:38:54 +0000145 static void unexpectedType(Type type, Class<?> expected) {
crazyboblee41bc8522006-12-08 07:06:55 +0000146 throw new AssertionError(
147 "Unexpected type. Expected: " + expected.getName()
crazyboblee0baa9fc2007-01-31 02:38:54 +0000148 + ", got: " + type.getClass().getName()
149 + ", for type literal: " + type.toString() + ".");
crazyboblee41bc8522006-12-08 07:06:55 +0000150 }
151
152 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +0000153 * Gets type literal for the given {@code Type} instance.
crazyboblee41bc8522006-12-08 07:06:55 +0000154 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000155 public static TypeLiteral<?> get(Type type) {
156 return new SimpleTypeLiteral<Object>(type);
crazyboblee41bc8522006-12-08 07:06:55 +0000157 }
158
159 /**
crazyboblee0baa9fc2007-01-31 02:38:54 +0000160 * Gets type literal for the given {@code Class} instance.
crazyboblee41bc8522006-12-08 07:06:55 +0000161 */
crazyboblee0baa9fc2007-01-31 02:38:54 +0000162 public static <T> TypeLiteral<T> get(Class<T> type) {
163 return new SimpleTypeLiteral<T>(type);
crazyboblee41bc8522006-12-08 07:06:55 +0000164 }
165
crazyboblee0baa9fc2007-01-31 02:38:54 +0000166 private static class SimpleTypeLiteral<T> extends TypeLiteral<T> {
167 public SimpleTypeLiteral(Type type) {
crazyboblee41bc8522006-12-08 07:06:55 +0000168 super(type);
169 }
170 }
171}