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