001/*
002 * Copyright 2015 Transmogrify LLC.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package com.soklet.converter;
018
019import javax.annotation.Nonnull;
020import javax.annotation.Nullable;
021import javax.annotation.concurrent.ThreadSafe;
022import java.lang.reflect.ParameterizedType;
023import java.lang.reflect.Type;
024import java.util.Objects;
025
026import static java.lang.String.format;
027
028/**
029 * Construct for creating type tokens that represent generic types.
030 * <p>
031 * In Java, you may express a type token for a non-generic type like {@code String.class}. But you cannot say
032 * <code>List&lt;String&gt;.class</code>. Using {@code TypeReference}, you can express the latter as follows:
033 * <p>
034 * <code>new TypeReference&lt;List&lt;String&gt;&gt;() &#123;&#125;</code>
035 * <p>
036 * See <a
037 * href="http://gafter.blogspot.com/2006/12/super-type-tokens.html">http://gafter.blogspot.com/2006/12/super-type-
038 * tokens.html</a> for more details.
039 *
040 * @author Neal Gafter
041 * @author Bob Lee
042 * @author <a href="https://www.revetkn.com">Mark Allen</a>
043 */
044@ThreadSafe
045public abstract class TypeReference<T> {
046        @Nonnull
047        private final Type type;
048
049        protected TypeReference() {
050                Type superclass = getClass().getGenericSuperclass();
051
052                if (superclass instanceof Class)
053                        throw new IllegalStateException("Missing type parameter.");
054
055                this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
056        }
057
058        @Override
059        @Nonnull
060        public String toString() {
061                return format("%s{type=%s}", getClass().getSimpleName(), getType());
062        }
063
064        @Override
065        public boolean equals(@Nullable Object object) {
066                if (this == object)
067                        return true;
068
069                if (!(object instanceof TypeReference<?> typeReference))
070                        return false;
071
072                return Objects.equals(getType(), typeReference.getType());
073        }
074
075        @Override
076        public int hashCode() {
077                return Objects.hash(getType());
078        }
079
080        @Nonnull
081        public Type getType() {
082                return this.type;
083        }
084}