001/* 002 * Copyright 2022-2025 Revetware 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; 018 019import javax.annotation.Nonnull; 020import java.lang.reflect.Parameter; 021 022import static java.util.Objects.requireNonNull; 023 024/** 025 * Contract for concrete instance generation given type information. 026 * <p> 027 * A standard threadsafe implementation can be acquired via the {@link #defaultInstance()} factory method. 028 * <p> 029 * See <a href="https://www.soklet.com/docs/instance-creation">https://www.soklet.com/docs/instance-creation</a> for detailed documentation. 030 * 031 * @author <a href="https://www.revetkn.com">Mark Allen</a> 032 */ 033@FunctionalInterface 034public interface InstanceProvider { 035 /** 036 * Vends an instance of the given class. 037 * <p> 038 * The instance does not necessarily have to be new for every invocation (for example, implementors might return cached instances). 039 * 040 * @param instanceClass type token which represents the class to instantiate 041 * @param <T> the type of class to instantiate 042 * @return an instance of {@code T} 043 */ 044 @Nonnull 045 <T> T provide(@Nonnull Class<T> instanceClass); 046 047 /** 048 * Vends an instance appropriate for the supplied {@link Parameter}. 049 * <p> 050 * This is useful for solving the "robot legs" problem, where you might examine a qualifying annotation on a parameter to disambiguate vended instances. 051 * <p> 052 * For example, given this <em>Resource Method</em>: 053 * <pre>{@code @GET("/robot-generator") 054 * public Robot robotGenerator( 055 * @Left LegFactory leftLegFactory, // Custom annotation 056 * @Right LegFactory rightLegFactory, // Custom annotation 057 * BodyFactory bodyFactory 058 * ) { 059 * // Build a robot 060 * LeftLeg leftLeg = leftLegFactory.buildLeg(); 061 * LeftLeg rightLeg = rightLegFactory.buildLeg(); 062 * Body body = bodyFactory.build(); 063 * return new Robot(leftLeg, rightLeg, body); 064 * }}</pre> 065 * <p> 066 * Your implementation might look like this: 067 * <pre>{@code @Override 068 * @SuppressWarnings("unchecked") 069 * public <T> T provide(@Nonnull Parameter parameter) { 070 * Class<T> type = (Class<T>) parameter.getType(); 071 * 072 * // Pick the appropriate LegFactory by inspecting annotations 073 * if (type == LegFactory.class) { 074 * if (parameter.isAnnotationPresent(Left.class)) 075 * return (T) LEFT_INSTANCE; 076 * if (parameter.isAnnotationPresent(Right.class)) 077 * return (T) RIGHT_INSTANCE; 078 * 079 * throw new IllegalArgumentException("LegFactory requires @Left or @Right"); 080 * } 081 * 082 * // No qualifier logic needed - use the class-based path 083 * return provide(type); 084 * }}</pre> 085 * <p> 086 * The default implementation delegates to {@link #provide(Class)} using the parameter's raw type. 087 * 088 * @param parameter the parameter instance to instantiate 089 * @param <T> the type of class to instantiate (by default, the value of {@link Parameter#getType()}) 090 * @return an instance of {@code T} 091 */ 092 @Nonnull 093 @SuppressWarnings("unchecked") 094 default <T> T provide(@Nonnull Parameter parameter) { 095 requireNonNull(parameter); 096 return provide((Class<T>) parameter.getType()); 097 } 098 099 /** 100 * Acquires a threadsafe {@link InstanceProvider} with a reflection-based {@code instanceClass.getDeclaredConstructor().newInstance()} instantiation strategy. 101 * <p> 102 * The returned instance is guaranteed to be a JVM-wide singleton. 103 * 104 * @return an {@code InstanceProvider} with a reflection-based instantiation strategy 105 */ 106 @Nonnull 107 static InstanceProvider defaultInstance() { 108 return DefaultInstanceProvider.defaultInstance(); 109 } 110}