{"record":{"id":"3b7e472260301111","repo":"apple/pkl","slug":"implementationtype-must-not-be-abstract-but-s","errorCode":null,"errorMessage":"`implementationType` must not be abstract, but `%s` is.","messagePattern":"`implementationType` must not be abstract, but `(.+?)` is\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pkl-config-java/src/main/java/org/pkl/config/java/mapper/TypeMapping.java","lineNumber":36,"sourceCode":"import java.lang.reflect.Modifier;\nimport org.jspecify.annotations.Nullable;\n\n/**\n * Maps a type requested during conversion to the implementation type to be instantiated. The\n * requested type is often an interface type. The implementation type is always a class type. A\n * typical example is mapping {@link java.util.List} to {@link java.util.ArrayList}.\n */\n// kept simple for now\n// if we wanted to have more sophisticated mappings, we could:\n// * support mapping factories/strategies (cf. ConverterFactory/Converter)\n// * support parameterized mappings (e.g. Set<MyEnum> -> EnumSet<MyEnum>)\npublic final class TypeMapping<S, T extends S> {\n  public final Class<S> requestedType;\n  public final Class<T> implementationType;\n\n  private TypeMapping(Class<S> requestedType, Class<T> implementationType) {\n    if (Modifier.isAbstract(implementationType.getModifiers())) {\n      throw new IllegalArgumentException(\n          String.format(\n              \"`implementationType` must not be abstract, but `%s` is.\",\n              implementationType.getTypeName()));\n    }\n    if (!requestedType.isAssignableFrom(implementationType)) {\n      throw new IllegalArgumentException(\n          String.format(\n              \"`implementationType` must be assignable to `requestedType`, but `%s` is not assignable to `%s`.\",\n              implementationType.getTypeName(), requestedType.getTypeName()));\n    }\n    if (requestedType.isArray() || implementationType.isArray()) {\n      throw new IllegalArgumentException(\"Type mappings are not supported for array types.\");\n    }\n    this.requestedType = requestedType;\n    this.implementationType = implementationType;\n  }\n\n  public static <S, T extends S> TypeMapping<S, T> of(","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/apple/pkl/blob/f3efcbfc9b60d30053b0536d664948d7aa1b8673/pkl-config-java/src/main/java/org/pkl/config/java/mapper/TypeMapping.java#L18-L54","documentation":"TypeMapping's private constructor rejects an implementationType that is an abstract class (Modifier.isAbstract). A type mapping needs a concrete class it can actually instantiate when converting Pkl values to Java objects, so passing an abstract class or interface as the implementation is invalid.","triggerScenarios":"Calling TypeMapping.of(SomeAbstract.class, AbstractImpl.class) or a factory that forwards to the private constructor with a Class whose getModifiers() include the abstract bit (abstract classes, interfaces, annotation types).","commonSituations":"Mapping a Pkl module to a Java interface or abstract base class instead of a concrete subclass; picking the wrong class constant (e.g. List.class instead of ArrayList.class).","solutions":["Pass a concrete, instantiable class as the implementationType.","If you only have the abstract type, select a concrete subclass to map to.","Check with Modifier.isAbstract(impl.getModifiers()) before constructing the mapping."],"exampleFix":"// before\nvar mapping = TypeMapping.of(Animal.class, Animal.class);\n// after\nvar mapping = TypeMapping.of(Animal.class, Dog.class);","handlingStrategy":"validation","validationCode":"if (Modifier.isAbstract(impl.getModifiers())) throw new IllegalArgumentException(impl + \" must be concrete\");","typeGuard":"static boolean isConcrete(Class<?> c) { return !c.isInterface() && !Modifier.isAbstract(c.getModifiers()); }","tryCatchPattern":"try { var m = TypeMapping.of(req, impl); } catch (IllegalArgumentException e) { /* surface which type was abstract */ }","preventionTips":["Prefer the compile-time generic form TypeMapping.of(S.class, T.class) with T extends S","Map interfaces to their concrete implementations, never to the interface itself"],"tags":["java","type-mapping","illegal-argument","reflection"],"backgroundTag":"invalid-argument-value","analyzedSha":"f3efcbfc9b60d30053b0536d664948d7aa1b8673","analyzedAt":"2026-09-08T13:10:45.570Z","contentChangedAt":"2026-09-08T13:10:45.570Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}