Repository: openrewrite/rewrite-dropwizard
Pull request #3: Superclass modifying recipes
Author: login: Fossur
Labels: name: recipe
Created: 2025-03-31T15:19:53Z | Merged: 2025-04-28T19:35:31Z

This has recipes to update/remove various supertypes basically. A big part of this is a recipe that can add method stubs when changing classes. Basically it also has various cleanup method and visitors to maintain type consistency after removing/updating methods - I did want it to be as generalizable as possible to also aid in other migrations potentially

## Anything in particular you'd like reviewers to focus on?
If there are cleaner ways of adding the methods, doing various type checks, then I would really be open to it. This is a big PR - so take your time - it made sense to bundle them because they are all somewhat related.

## Anyone you would like to review specifically?
@timtebeek 

## Any additional context

I also have some general questions regarding the migrations, basically how do we want to add new files like Security Configuration or helper classes - there are a couple of ways we could go about it 1) We can add files via recipes like [this](https://docs.openrewrite.org/recipes/text/createtextfile) - essentially would work, but 1) not good for multi-module projects 2) we do not know where to put those files 3) We lose the type information and these can't really be operated on. The other way is a helper library, where we could put those files, like SecurityConfiguration, a DAO helper class - would you be open to this idea? Any alternative are welcome as well

### Checklist
- [X] I've added unit tests to cover both positive and negative cases
- [X] I've read and applied the [recipe conventions and best practices](https://docs.openrewrite.org/authoring-recipes/recipe-conventions-and-best-practices)
- [X] I've used the IntelliJ IDEA auto-formatter on affected files



Diff:
diff --git a/README.md b/README.md
index ba095f8..8c773a5 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
-<p align="center">
+                                                                                                    <p align="center">
+
   <a href="https://docs.openrewrite.org">
     <picture>
       <source media="(prefers-color-scheme: dark)" srcset="https://github.com/openrewrite/rewrite/raw/main/doc/logo-oss-dark.svg">
@@ -23,10 +24,13 @@
 
 ### What is this?
 
-This project implements a [Rewrite module](https://github.com/openrewrite/rewrite) that applies best practices and migrations for those projects using the [Dropwizard framework](https://dropwizard.io/).
+This project implements a [Rewrite module](https://github.com/openrewrite/rewrite) that applies best practices and
+migrations for those projects using the [Dropwizard framework](https://dropwizard.io/).
 
 Browse [a selection of recipes available through this module in the recipe catalog](https://docs.openrewrite.org/recipes/java/dropwizard).
 
 ## Contributing
 
-We appreciate all types of contributions. See the [contributing guide](https://github.com/openrewrite/.github/blob/main/CONTRIBUTING.md) for detailed instructions on how to get started.
+We appreciate all types of contributions. See
+the [contributing guide](https://github.com/openrewrite/.github/blob/main/CONTRIBUTING.md) for detailed instructions on
+how to get started.
diff --git a/build.gradle.kts b/build.gradle.kts
index c8f2771..9a036e8 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -26,9 +26,11 @@ dependencies {
     testImplementation("org.openrewrite:rewrite-test")
 
     testRuntimeOnly("io.dropwizard.metrics:metrics-annotation:4.1.+")
+    testRuntimeOnly("io.dropwizard.metrics:metrics-healthchecks:4.1.+")
     testRuntimeOnly("org.springframework.boot:spring-boot-starter-actuator:2.5.+")
     testRuntimeOnly("javax.persistence:javax.persistence-api:2.2")
     testRuntimeOnly("org.projectlombok:lombok:1.18.+")
+    testRuntimeOnly("net.sourceforge.argparse4j:argparse4j:0.9.0")
 }
 
 recipeDependencies {
diff --git a/src/main/java/org/openrewrite/java/dropwizard/method/ChangeSuperType.java b/src/main/java/org/openrewrite/java/dropwizard/method/ChangeSuperType.java
new file mode 100644
index 0000000..d843ea4
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/dropwizard/method/ChangeSuperType.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.ShortenFullyQualifiedTypeReferences;
+import org.openrewrite.java.tree.Expression;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+
+import static java.lang.Boolean.TRUE;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isOfClassType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class ChangeSuperType extends Recipe {
+
+    @Option(displayName = "Target class",
+            description = "The fully qualified name of the class whose superclass should be changed.",
+            example = "com.myorg.MyClass")
+    String targetClass;
+
+    @Option(displayName = "New superclass",
+            description = "The fully qualified name of the new superclass to extend or interface to implement.",
+            example = "com.myorg.NewSuperclass")
+    String newSuperclass;
+
+    @Option(displayName = "Keep type parameters",
+            description = "Whether to keep existing type parameters on the target class declaration.",
+            required = false)
+    Boolean keepTypeParameters;
+
+    @Option(displayName = "Convert to interface",
+            description = "If the new supertype is an interface, setting this to true converts 'extends' to 'implements'.",
+            required = false)
+    Boolean convertToInterface;
+
+    @Option(displayName = "Remove unnecessary overrides",
+            description = "Remove method Override annotations that override methods from the *old* superclass but are no longer necessary with the new superclass.",
+            required = false)
+    Boolean removeUnnecessaryOverrides;
+
+    @Override
+    public String getDisplayName() {
+        return "Change superclass";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Changes the superclass of a specified class to a new superclass.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new JavaIsoVisitor<ExecutionContext>() {
+            @Override
+            public J.ClassDeclaration visitClassDeclaration(
+                    J.ClassDeclaration classDecl, ExecutionContext ctx) {
+                J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+                if (cd.getExtends() == null || !isOfClassType(cd.getExtends().getType(), targetClass)) {
+                    return cd;
+                }
+
+                String typeParams = getTypeParams(cd.getExtends());
+
+                maybeAddImport(newSuperclass);
+                maybeRemoveImport(targetClass);
+
+                JavaTemplate extendsTemplate = JavaTemplate.builder(newSuperclass + typeParams)
+                        // TODO runtimeClasspath() might be different in other recipe run environments
+                        .javaParser(JavaParser.fromJavaVersion().classpath(JavaParser.runtimeClasspath()))
+                        .imports(newSuperclass)
+                        .contextSensitive()
+                        .build();
+
+                JavaType.FullyQualified newSuperType;
+
+                if (TRUE.equals(convertToInterface)) {
+                    cd = extendsTemplate.apply(updateCursor(cd), cd.getCoordinates().addImplementsClause());
+                    cd = cd.withExtends(null);
+                    TypeTree lastInterface = cd.getImplements().get(cd.getImplements().size() - 1);
+                    newSuperType = asFullyQualified(lastInterface.getType());
+                } else {
+                    cd = extendsTemplate.apply(updateCursor(cd), cd.getCoordinates().replaceExtendsClause());
+                    newSuperType = asFullyQualified(cd.getExtends().getType());
+                }
+
+                // If changing to a class that was not compiled initially (eg. from a new library)
+                if (newSuperType == null) {
+                    newSuperType = asFullyQualified(JavaType.buildType(newSuperclass));
+                }
+
+                cd = cd.withType(((JavaType.Class) cd.getType()).withSupertype(newSuperType));
+                doAfterVisit(ShortenFullyQualifiedTypeReferences.modifyOnly(cd));
+                doAfterVisit(new UpdateMethodTypesVisitor(cd.getType()));
+
+                if (TRUE.equals(removeUnnecessaryOverrides)) {
+                    doAfterVisit(new RemoveUnnecessaryOverride(false).getVisitor());
+                }
+
+                doAfterVisit(new RemoveUnnecessarySuperCalls.RemoveUnnecessarySuperCallsVisitor());
+                return cd;
+            }
+
+            private String getTypeParams(TypeTree extendsType) {
+                StringBuilder typeParams = new StringBuilder();
+
+                if (TRUE.equals(keepTypeParameters) && extendsType instanceof J.ParameterizedType) {
+                    J.ParameterizedType parameterizedType = (J.ParameterizedType) extendsType;
+
+                    boolean hasParameters = false;
+                    typeParams.append('<');
+
+                    for (Expression typeParameter : parameterizedType.getTypeParameters()) {
+                        if (hasParameters) {
+                            typeParams.append(", ");
+                        }
+                        typeParams.append(typeParameter.toString());
+                        hasParameters = true;
+                    }
+
+                    if (hasParameters) {
+                        typeParams.append('>');
+                    } else {
+                        typeParams.setLength(0);
+                    }
+                }
+
+                return typeParams.toString();
+            }
+        };
+    }
+}
diff --git a/src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByType.java b/src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByType.java
new file mode 100644
index 0000000..7936464
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByType.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.search.UsesType;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+
+import static org.openrewrite.java.tree.TypeUtils.isOfClassType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByType extends Recipe {
+
+    @Option(displayName = "Fully qualified name of the superclass to remove",
+            description = "Supertypes that match this name are to be removed",
+            example = "io.dropwizard.Configuration")
+    String typeToRemove;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove supertype by fully qualified name matches";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes a specified type from class extends or implements clauses.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return Preconditions.check(
+                new UsesType<>(typeToRemove, false),
+                new JavaIsoVisitor<ExecutionContext>() {
+                    @Override
+                    public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
+                        J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+                        if (cd.getExtends() != null && isOfClassType(cd.getExtends().getType(), typeToRemove)) {
+                            cd = cd.withExtends(null);
+                            JavaType.ShallowClass type = (JavaType.ShallowClass) JavaType.buildType("java.lang.Object");
+                            doAfterVisit(new UpdateMethodTypesVisitor(type));
+                            doAfterVisit(new RemoveUnnecessarySuperCalls.RemoveUnnecessarySuperCallsVisitor());
+                        }
+
+                        return cd;
+                    }
+
+                }
+        );
+    }
+}
diff --git a/src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java b/src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
new file mode 100644
index 0000000..a6442c7
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.RemoveAnnotationVisitor;
+import org.openrewrite.java.search.UsesType;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
+
+    private static final AnnotationMatcher OVERRIDE_ANNOTATION = new AnnotationMatcher("@java.lang.Override");
+    private static final RemoveAnnotationVisitor removeAnnotationVisitor = new RemoveAnnotationVisitor(OVERRIDE_ANNOTATION);
+
+    @Option(
+            displayName = "Ignore methods in anonymous classes",
+            description = "When enabled, ignore @Override annotations on methods in anonymous classes.",
+            required = false)
+    @Nullable
+    Boolean ignoreAnonymousClassMethods;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove unnecessary `@Override` annotations";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes `@Override` annotations from methods that don't actually override or implement any method. " +
+                "This helps maintain clean code by removing incorrect annotations that could be misleading.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return Preconditions.check(
+                new UsesType<>("java.lang.Override", false),
+                new JavaIsoVisitor<ExecutionContext>() {
+                    private Cursor getCursorToParentScope(Cursor cursor) {
+                        return cursor.dropParentUntil(is -> is instanceof J.NewClass || is instanceof J.ClassDeclaration);
+                    }
+
+                    @Override
+                    public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
+                        J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
+                        if (!m.isConstructor() &&
+                                service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION) &&
+                                !TypeUtils.isOverride(m.getMethodType()) &&
+                                !(Boolean.TRUE.equals(ignoreAnonymousClassMethods) &&
+                                        getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) {
+                            return removeAnnotationVisitor.visitMethodDeclaration(m, ctx);
+                        }
+                        return m;
+                    }
+                });
+    }
+}
diff --git a/src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCalls.java b/src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCalls.java
new file mode 100644
index 0000000..7e915d5
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCalls.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeUtils;
+
+public class RemoveUnnecessarySuperCalls extends Recipe {
+
+    @Override
+    public String getDisplayName() {
+        return "Remove `super` calls when the class does not extend another class";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes calls to `super(...)` or `super.someMethod(...)` if the class does not have a real superclass besides `java.lang.Object`.";
+    }
+
+    @Override
+    public JavaIsoVisitor<ExecutionContext> getVisitor() {
+        return new RemoveUnnecessarySuperCallsVisitor();
+    }
+
+    public static class RemoveUnnecessarySuperCallsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        @Override
+        public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
+            J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
+
+            if (isSuperCall(mi)) {
+                J.ClassDeclaration classDecl = getCursor().firstEnclosing(J.ClassDeclaration.class);
+                if (classDecl != null) {
+                    if (!hasRealSuperclass(classDecl)) {
+                        return null;
+                    }
+                    if (!TypeUtils.isOverride(mi.getMethodType())) {
+                        return null;
+                    }
+                }
+            }
+            return mi;
+        }
+
+        /**
+         * Checks if this is a call on `super`, like `super(...)` or `super.someMethod(...)`.
+         */
+        private boolean isSuperCall(J.MethodInvocation mi) {
+            // e.g. super.method()
+            if (mi.getSelect() instanceof J.Identifier) {
+                return "super".equals(((J.Identifier) mi.getSelect()).getSimpleName());
+            }
+            // e.g. constructor call "super(...)"
+            return mi.getSelect() == null && "super".equals(mi.getSimpleName());
+        }
+
+        /**
+         * Checks if the class extends some real superclass other than `java.lang.Object`.
+         */
+        private boolean hasRealSuperclass(J.ClassDeclaration classDecl) {
+            if (classDecl.getExtends() == null) {
+                return false;
+            }
+
+            // If we can't resolve types, we might choose to be safe and not remove
+            JavaType.FullyQualified fqClassType = TypeUtils.asFullyQualified(classDecl.getType());
+            if (fqClassType == null) {
+                return true;
+            }
+
+            JavaType.FullyQualified fqSuperType = TypeUtils.asFullyQualified(fqClassType.getSupertype());
+            if (fqSuperType == null) {
+                return false;
+            }
+
+            // If the only "superclass" is Object, treat as no real parent
+            return !"java.lang.Object".equals(fqSuperType.getFullyQualifiedName());
+        }
+    }
+}
diff --git a/src/main/java/org/openrewrite/java/dropwizard/method/UpdateMethodTypesVisitor.java b/src/main/java/org/openrewrite/java/dropwizard/method/UpdateMethodTypesVisitor.java
new file mode 100644
index 0000000..6a2c6ed
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/dropwizard/method/UpdateMethodTypesVisitor.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+
+public class UpdateMethodTypesVisitor extends JavaIsoVisitor<ExecutionContext> {
+    private final JavaType.FullyQualified newSuperclass;
+
+    public UpdateMethodTypesVisitor(JavaType.FullyQualified newSuperclass) {
+        this.newSuperclass = newSuperclass;
+    }
+
+    @Override
+    public J.MethodDeclaration visitMethodDeclaration(
+            J.MethodDeclaration method, ExecutionContext ctx) {
+        JavaType.Method methodType = method.getMethodType();
+
+        if (methodType != null) {
+
+            boolean hasOverrideAnnotation =
+                    method.getLeadingAnnotations().stream()
+                            .anyMatch(annotation -> "Override".equals(annotation.getSimpleName()));
+
+            if (hasOverrideAnnotation) {
+                JavaType.FullyQualified declaringType = methodType.getDeclaringType();
+                if (declaringType != null) {
+                    JavaType.Method originalMethodType = method.getMethodType();
+                    if (originalMethodType != null) {
+                        JavaType.Method newMethodType = originalMethodType.withDeclaringType(newSuperclass);
+                        method = method.withMethodType(newMethodType);
+                        method = method.withName(method.getName().withType(newMethodType));
+                    }
+                    return method;
+                }
+            }
+        }
+        return method;
+    }
+}
diff --git a/src/main/java/org/openrewrite/java/dropwizard/method/package-info.java b/src/main/java/org/openrewrite/java/dropwizard/method/package-info.java
new file mode 100644
index 0000000..3e86d02
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/dropwizard/method/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+@NullMarked
+@NonNullFields
+package org.openrewrite.java.dropwizard.method;
+
+import org.jspecify.annotations.NullMarked;
+import org.openrewrite.internal.lang.NonNullFields;
diff --git a/src/main/resources/META-INF/rewrite/dropwizard-to-spring-boot.yml b/src/main/resources/META-INF/rewrite/dropwizard-to-spring-boot.yml
index 50c9145..13a3622 100644
--- a/src/main/resources/META-INF/rewrite/dropwizard-to-spring-boot.yml
+++ b/src/main/resources/META-INF/rewrite/dropwizard-to-spring-boot.yml
@@ -26,7 +26,6 @@ recipeList:
   - org.openrewrite.java.dropwizard.MigrateResourcesToSpringJersey
   - org.openrewrite.java.dropwizard.MigrateTests
   - org.openrewrite.java.dropwizard.CodeCleanup
-  - org.openrewrite.java.dropwizard.annotation.micrometer.CodahaleTimedToMicrometerTimed
 ---
 # Core Application Setup
 type: specs.openrewrite.org/v1beta/recipe
@@ -50,6 +49,8 @@ recipeList:
   - org.openrewrite.java.dropwizard.annotation.AddClassAnnotationIfSuperTypeExists:
       annotationToAdd: "org.springframework.boot.SpringBootApplication"
       targetSupertypeName: "io.dropwizard.Application"
+  - org.openrewrite.java.dropwizard.method.RemoveSuperTypeByType:
+      typeToRemove: io.dropwizard.Application
 
 ---
 type: specs.openrewrite.org/v1beta/recipe
@@ -62,6 +63,7 @@ recipeList:
       artifactId: spring-boot-starter-actuator
       version: "2.7.18"
       onlyIfAbsent: true
+  - org.openrewrite.java.dropwizard.annotation.micrometer.CodahaleTimedToMicrometerTimed
 
 ---
 type: specs.openrewrite.org/v1beta/recipe
@@ -72,6 +74,12 @@ recipeList:
   - org.openrewrite.java.dropwizard.annotation.AddClassAnnotationIfSuperTypeExists:
       annotationToAdd: "org.springframework.stereotype.Component"
       targetSupertypeName: "org.springframework.boot.actuate.health.HealthIndicator"
+  - org.openrewrite.java.dropwizard.method.ChangeSuperType:
+      targetClass: com.codahale.metrics.health.HealthCheck
+      newSuperclass: org.springframework.boot.actuate.health.HealthIndicator
+      convertToInterface: true
+      keepTypeParameters: false
+      removeUnnecessaryOverrides: true
 ---
 # Configuration Migration
 type: specs.openrewrite.org/v1beta/recipe
@@ -92,6 +100,8 @@ recipeList:
   - org.openrewrite.java.dropwizard.annotation.AddClassAnnotationIfAnnotationExists:
       annotationToAdd: "org.springframework.stereotype.Component"
       targetAnnotationClassName: "javax.ws.rs.ext.Provider"
+  - org.openrewrite.java.dropwizard.method.RemoveSuperTypeByType:
+      typeToRemove: "io.dropwizard.Configuration"
 ---
 # Resource Migration
 type: specs.openrewrite.org/v1beta/recipe
@@ -135,6 +145,12 @@ recipeList:
   - org.openrewrite.java.dropwizard.annotation.AddClassAnnotationIfSuperTypeExists:
       annotationToAdd: "org.springframework.transaction.annotation.Transactional"
       targetSupertypeName: "io.dropwizard.hibernate.AbstractDAO"
+#  - org.openrewrite.java.dropwizard.method.ChangeSuperType:
+#      targetClass: "io.dropwizard.hibernate.AbstractDAO"
+#      newSuperclass: "ee.taltech.example.AbstractJpaDAO" # Custom class; from helper library
+#      convertToInterface: false
+#      keepTypeParameters: true
+#      removeUnnecessaryOverrides: true
 ---
 # Test migration
 type: specs.openrewrite.org/v1beta/recipe
@@ -161,6 +177,21 @@ recipeList:
       version: "2.7.18"
       onlyIfAbsent: true
 ---
+# Tasks, Commands
+type: specs.openrewrite.org/v1beta/recipe
+name: org.openrewrite.java.dropwizard.MigrateTasksAndCommands
+displayName: Clean up various issues with the code.
+description: Clean up the code.
+recipeList:
+  - org.openrewrite.java.dropwizard.method.ChangeSuperclass:
+      targetClass: io.dropwizard.cli.ConfiguredCommand
+      newSuperclass: org.springframework.boot.CommandLineRunner
+      convertToInterface: true
+      keepTypeParameters: false
+      removeUnnecessaryOverrides: true
+  - org.openrewrite.java.dropwizard.method.RemoveSuperTypeByType:
+      typeToRemove: io.dropwizard.servlets.tasks.PostBodyTask
+---
 # Cleanup
 type: specs.openrewrite.org/v1beta/recipe
 name: org.openrewrite.java.dropwizard.CodeCleanup
@@ -168,8 +199,11 @@ displayName: Clean up various issues with the code.
 description: Clean up the code.
 recipeList:
   - org.openrewrite.java.ShortenFullyQualifiedTypeReferences
-  # TODO - org.openrewrite.java.dropwizard.method.AddMissingAbstractMethods
   - org.openrewrite.java.RemoveUnusedImports
+  - org.openrewrite.java.dropwizard.method.RemoveUnnecessaryOverride:
+      ignoreAnonymousClassMethods: false
+  - org.openrewrite.maven.OrderPomElements
+  - org.openrewrite.maven.RemoveRedundantDependencyVersions
 ---
 type: specs.openrewrite.org/v1beta/recipe
 name: org.openrewrite.java.dropwizard.annotation.micrometer.CodahaleTimedToMicrometerTimed
diff --git a/src/main/resources/META-INF/rewrite/examples.yml b/src/main/resources/META-INF/rewrite/examples.yml
index e54f200..370ead7 100644
--- a/src/main/resources/META-INF/rewrite/examples.yml
+++ b/src/main/resources/META-INF/rewrite/examples.yml
@@ -107,3 +107,124 @@ examples:
           }
       }
     language: java
+---
+type: specs.openrewrite.org/v1beta/example
+recipeName: org.openrewrite.java.dropwizard.method.ChangeSuperType
+examples:
+- description: ''
+  parameters:
+  - java.util.Vector
+  - java.util.ArrayList
+  - 'false'
+  - 'false'
+  - 'false'
+  sources:
+  - before: |
+      package org.example;
+
+      public class Child extends java.util.Vector<String> {
+          public void someMethod() {
+              this.add("test");
+          }
+      }
+    after: |
+      package org.example;
+
+      import java.util.ArrayList;
+
+      public class Child extends ArrayList {
+          public void someMethod() {
+              this.add("test");
+          }
+      }
+    language: java
+---
+type: specs.openrewrite.org/v1beta/example
+recipeName: org.openrewrite.java.dropwizard.method.RemoveSuperTypeByType
+examples:
+- description: ''
+  parameters:
+  - com.example.BaseClass
+  sources:
+  - before: |
+      package com.example;
+
+      class BaseClass {
+          protected void baseMethod() {}
+      }
+    language: java
+  - before: |
+      package com.example;
+
+      class ChildClass extends BaseClass {
+          void someMethod() {
+              baseMethod();
+          }
+      }
+    after: |
+      package com.example;
+
+      class ChildClass {
+          void someMethod() {
+              baseMethod();
+          }
+      }
+    language: java
+---
+type: specs.openrewrite.org/v1beta/example
+recipeName: org.openrewrite.java.dropwizard.method.RemoveUnnecessaryOverride
+examples:
+- description: ''
+  parameters:
+  - 'false'
+  sources:
+  - before: |
+      import net.sourceforge.argparse4j.impl.Arguments;
+      import net.sourceforge.argparse4j.inf.Subparser;
+      import org.springframework.boot.CommandLineRunner;
+
+      public class RenderCommand implements CommandLineRunner {
+          @Override
+          public void configure(Subparser subparser) {
+              subparser.addArgument("-i", "--include-default")
+                       .action(Arguments.storeTrue())
+                       .dest("include-default")
+                       .help("Also render the template with the default name");
+              subparser.addArgument("names").nargs("*");
+          }
+      }
+    after: |
+      import net.sourceforge.argparse4j.impl.Arguments;
+      import net.sourceforge.argparse4j.inf.Subparser;
+      import org.springframework.boot.CommandLineRunner;
+
+      public class RenderCommand implements CommandLineRunner {
+          public void configure(Subparser subparser) {
+              subparser.addArgument("-i", "--include-default")
+                       .action(Arguments.storeTrue())
+                       .dest("include-default")
+                       .help("Also render the template with the default name");
+              subparser.addArgument("names").nargs("*");
+          }
+      }
+    language: java
+---
+type: specs.openrewrite.org/v1beta/example
+recipeName: org.openrewrite.java.dropwizard.method.RemoveUnnecessarySuperCalls
+examples:
+- description: ''
+  sources:
+  - before: |
+      class A {
+          public String toString() {
+              super.toString();
+              return "hello";
+          }
+      }
+    after: |
+      class A {
+          public String toString() {
+              return "hello";
+          }
+      }
+    language: java
diff --git a/src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java b/src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
new file mode 100644
index 0000000..0d2e3df
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesGenerics() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "org.example.OldParent", "org.example.NewParent", true, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class OldParent<T> {
+                  protected T value;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class NewParent<T> {
+                  protected T value;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesSuperCallWithOverrides() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+
+                  public OldParent(String test) {
+                  }
+
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+
+                  public Child() {
+                      super("test");
+                  }
+
+                  @Override
+                  protected void doSomething() {
+                      super.doSomething();
+                      System.out.println("child");
+                      super.doSomethingElse();
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+
+                  public Child() {
+                  }
+
+                  protected void doSomething() {
+                      System.out.println("child");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    @Disabled
+    @Deprecated
+    void removesConstructorSuperCall() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  public OldParent(String value) {}
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  public NewParent(String value) {}
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public Child(String value) {
+                      super(value);
+                      System.out.println("child constructor");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public Child(String value) {
+                      System.out.println("child constructor");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changeExtendsToImplements() {
+        rewriteRun(
+          spec ->
+            spec.recipe(
+              new ChangeSuperType(
+                "com.codahale.metrics.health.HealthCheck",
+                "org.springframework.boot.actuate.health.HealthIndicator",
+                false,
+                true,
+                true)),
+          java(
+            """
+              import com.codahale.metrics.health.HealthCheck;
+
+              public class TemplateHealthCheck extends HealthCheck {
+
+                  @Override
+                  protected Result check() throws Exception {
+                      return Result.healthy();
+                  }
+              }
+              """,
+            """
+              import org.springframework.boot.actuate.health.HealthIndicator;
+
+              public class TemplateHealthCheck implements HealthIndicator {
+
+                  protected Result check() throws Exception {
+                      return Result.healthy();
+                  }
+              }
+              """
+          )
+        );
+    }
+}
diff --git a/src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java b/src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
new file mode 100644
index 0000000..596152e
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @DocumentExample
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesGenericType() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass<T> {
+                  protected T getValue() { return null; }
+              }
+              """
+          ),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass<String> {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotModifyUnrelatedClasses() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class DifferentBase {
+                  protected void differentMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              package com.example;
+
+              class UnrelatedClass extends DifferentBase {
+                  void someMethod() {
+                      differentMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    @Disabled
+    void handlesImportRemoval() {
+        rewriteRun(
+          java(
+            """
+              package com.example.base;
+
+              public class BaseClass {
+                  protected void importedMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              package com.example.child;
+
+              import com.example.base.BaseClass;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      importedMethod();
+                  }
+              }
+              """,
+            """
+              package com.example.child;
+
+              class ChildClass {
+                  void someMethod() {
+                      importedMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+}
diff --git a/src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java b/src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
new file mode 100644
index 0000000..44d4722
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  void nonExistentMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesAnonymousClass() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnnecessaryOverride(true)),
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass {
+                  void test() {
+                      BaseClass anon = new BaseClass() {
+                          @Override
+                          void wrongMethod() {}
+                      };
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesSuperInterface() {
+        rewriteRun(
+          java(
+            """
+              interface SuperInterface {
+                  void superMethod();
+              }
+
+              interface SubInterface extends SuperInterface {
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass implements SubInterface {
+                  @Override
+                  public void superMethod() {}
+
+                  @Override
+                  public void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass implements SubInterface {
+                  @Override
+                  public void superMethod() {}
+
+                  public void nonExistentMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOverrideFromNonexistentSuper() {
+        rewriteRun(
+          spec -> spec.parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              class TestClass {
+                  @Override
+                  public void execute() {
+
+                  }
+              }
+              """,
+            """
+              class TestClass {
+                  public void execute() {
+
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOverrideFromChangedSuper() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+                new RemoveSuperTypeByType("ee.test.BaseClass"),
+                new RemoveUnnecessaryOverride(false))
+              .parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              package ee.test;
+
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              package ee.test;
+
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """,
+            """
+              package ee.test;
+
+              class TestClass {
+                  void baseMethod() {}
+              }
+              """
+          )
+        );
+    }
+}
diff --git a/src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCallsTest.java b/src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCallsTest.java
new file mode 100644
index 0000000..fc97b61
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCallsTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessarySuperCallsTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessarySuperCalls());
+    }
+
+    @DocumentExample
+    @Test
+    void removesSuperCallsWhenNoSuperclass() {
+        rewriteRun(
+          java(
+            """
+              class A {
+                  public String toString() {
+                      super.toString();
+                      return "hello";
+                  }
+              }
+              """,
+            """
+              class A {
+                  public String toString() {
+                      return "hello";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsSuperCallsWhenMethodIsOverridden() {
+        rewriteRun(
+          java(
+            """
+              class Parent {
+                  void method() {}
+              }
+              """
+          ),
+          java(
+            """
+              class Child extends Parent {
+                  @Override
+                  void method() {
+                      super.method();
+                      System.out.println("hello");
+                  }
+              }
+              """,
+            """
+              class Child extends Parent {
+                  @Override
+                  void method() {
+                      System.out.println("hello");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesSuperConstructorCallWhenNoSuperclass() {
+        rewriteRun(
+          java(
+            """
+              class A {
+                  public A() {
+                      super();
+                      System.out.println("initializing");
+                  }
+              }
+              """,
+            """
+              class A {
+                  public A() {
+                      System.out.println("initializing");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsSuperConstructorCallWhenExtendingClass() {
+        rewriteRun(
+          java(
+            """
+              class Child {
+                  public Child() {
+                      super();
+                      System.out.println("child init");
+                  }
+              }
+              """,
+            """
+              class Child {
+                  public Child() {
+                      System.out.println("child init");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesSuperConstructorCallWithArgumentsWhenNoSuperclass() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType("old.Super", "new.Super", false, false, false),
+              new RemoveUnnecessarySuperCalls()),
+          java(
+            """
+              class A {
+                  public A(String name) {
+                      super("test");
+                      System.out.println("Hello " + name);
+                  }
+              }
+              """,
+            """
+              class A {
+                  public A(String name) {
+                      System.out.println("Hello " + name);
+                  }
+              }
+              """
+          )
+        );
+    }
+}


Reviews:
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-31T15:21:45Z
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T05:07:44Z
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T05:10:46Z
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T19:38:01Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T20:17:45Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T20:19:47Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T20:28:11Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T20:58:13Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T21:00:54Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-07T21:02:24Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T11:06:28Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T11:08:13Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T11:11:47Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T11:22:45Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T11:36:26Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T12:27:24Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T12:28:34Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-08T14:06:42Z
user: login: Fossur
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-14T18:17:34Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* build.gradle.kts
  * lines 14-13
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T18:27:25Z
user: login: github-actions[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T11:22:59Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T18:04:59Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T18:23:54Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T18:49:11Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T18:58:01Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:01:37Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:04:54Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-28T19:06:09Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:11:35Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:18:28Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:20:56Z
user: login: github-actions[bot]
body: Some suggestions could not be made:
* src/main/resources/META-INF/rewrite/examples.yml
  * lines 110-109
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:31:41Z
user: login: timtebeek
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-28T19:34:01Z
user: login: timtebeek
body: Thanks again for the continued work here @Fossur , and the patience with the reviews. I've taken some shortcuts to be able to get this in right now, be stripping out any (up to this point) unnecessary steps such that there's less to review and maintain. I hope you agree!
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-28T19:35:23Z

Review comments:
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
                        if (!bounds.isEmpty() &&
                                !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
```
created_at: 2025-03-31T15:21:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
+                            return false;
+                        }
+                    }
+                } else if (!isAssignableTo(existingParam, candidateParam)) {
+                    return false;
+                }
+            }
+
+            // Check return type
+            JavaType existingReturn = existing.getReturnType();
+            JavaType candidateReturn = candidate.getReturnType();
+
+            if (existingReturn instanceof JavaType.Parameterized
+                    && candidateReturn instanceof JavaType.Parameterized) {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
            if (existingReturn instanceof JavaType.Parameterized &&
                    candidateReturn instanceof JavaType.Parameterized) {
```
created_at: 2025-03-31T15:21:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
+                            return false;
+                        }
+                    }
+                } else if (!isAssignableTo(existingParam, candidateParam)) {
+                    return false;
+                }
+            }
+
+            // Check return type
+            JavaType existingReturn = existing.getReturnType();
+            JavaType candidateReturn = candidate.getReturnType();
+
+            if (existingReturn instanceof JavaType.Parameterized
+                    && candidateReturn instanceof JavaType.Parameterized) {
+                JavaType.Parameterized existingParamReturn = (JavaType.Parameterized) existingReturn;
+                JavaType.Parameterized candidateParamReturn = (JavaType.Parameterized) candidateReturn;
+
+                // Compare the raw types (e.g., Optional)
+                if (!isAssignableTo(existingParamReturn.getType(), candidateParamReturn.getType())) {
+                    return false;
+                }
+
+                // Compare type arguments
+                List<JavaType> existingTypeArgs = existingParamReturn.getTypeParameters();
+                List<JavaType> candidateTypeArgs = candidateParamReturn.getTypeParameters();
+
+                if (existingTypeArgs.size() != candidateTypeArgs.size()) {
+                    return false;
+                }
+
+                for (int i = 0; i < existingTypeArgs.size(); i++) {
+                    JavaType existingArg = existingTypeArgs.get(i);
+                    JavaType candidateArg = candidateTypeArgs.get(i);
+
+                    if (candidateArg instanceof JavaType.GenericTypeVariable) {
+                        String genericName = ((JavaType.GenericTypeVariable) candidateArg).getName();
+                        if (typeMapping.containsKey(genericName)) {
+                            JavaType mappedType = typeMapping.get(genericName);
+                            if (!isAssignableTo(existingArg, mappedType)) {
+                                return false;
+                            }
+                        }
+                    } else if (!isAssignableTo(existingArg, candidateArg)) {
+                        return false;
+                    }
+                }
+            } else return isAssignableTo(existingReturn, candidateReturn);
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
            } else {
                return isAssignableTo(existingReturn, candidateReturn);
            }
```
created_at: 2025-03-31T15:21:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,67 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.tree.JavaType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByPackage extends Recipe {
+
+    @Option(displayName = "Package to match",
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackage.java
user: login: github-actions[bot]
body: ```suggestion
    @Option(example = "TODO Provide a usage example for the docs", displayName = "Package to match",
```
created_at: 2025-03-31T15:21:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,64 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.tree.JavaType;
+
+import static org.openrewrite.java.tree.TypeUtils.isOfClassType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByType extends Recipe {
+
+    @Option(displayName = "Fully qualified name of the superclass to remove",
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByType.java
user: login: github-actions[bot]
body: ```suggestion
    @Option(example = "TODO Provide a usage example for the docs", displayName = "Fully qualified name of the superclass to remove",
```
created_at: 2025-03-31T15:21:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,75 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.nonNull;
+
+public abstract class RemoveSuperTypeVisitor extends JavaIsoVisitor<ExecutionContext> {
+
+    protected abstract boolean shouldRemoveType(JavaType type);
+
+    @Override
+    public J.ClassDeclaration visitClassDeclaration(
+            J.ClassDeclaration classDecl, ExecutionContext ctx) {
+        J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+        JavaType.FullyQualified fqn =
+                TypeUtils.asFullyQualified(JavaType.buildType("java.lang.Object"));
+        boolean modified = false;
+
+        // Handle extends clause
+        if (nonNull(cd.getExtends())
+                && nonNull(cd.getExtends().getType())
+                && shouldRemoveType(cd.getExtends().getType())) {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeVisitor.java
user: login: github-actions[bot]
body: ```suggestion
        if (nonNull(cd.getExtends()) &&
                nonNull(cd.getExtends().getType()) &&
                shouldRemoveType(cd.getExtends().getType())) {
```
created_at: 2025-03-31T15:21:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
import lombok.Value;
import org.jspecify.annotations.Nullable;
```
created_at: 2025-03-31T15:21:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
import java.util.ArrayList;
```
created_at: 2025-03-31T15:21:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
+
+    @Option(
+            displayName = "Ignore methods in anonymous classes",
+            description = "When enabled, ignore @Override annotations on methods in anonymous classes.",
+            required = false)
+    @Nullable
+    Boolean ignoreAnonymousClassMethods;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove unnecessary `@Override` annotations";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes `@Override` annotations from methods that don't actually override or implement any method. "
+                + "This helps maintain clean code by removing incorrect annotations that could be misleading.";
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
        return "Removes `@Override` annotations from methods that don't actually override or implement any method. " +
                "This helps maintain clean code by removing incorrect annotations that could be misleading.";
```
created_at: 2025-03-31T15:21:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
+
+    @Option(
+            displayName = "Ignore methods in anonymous classes",
+            description = "When enabled, ignore @Override annotations on methods in anonymous classes.",
+            required = false)
+    @Nullable
+    Boolean ignoreAnonymousClassMethods;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove unnecessary `@Override` annotations";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes `@Override` annotations from methods that don't actually override or implement any method. "
+                + "This helps maintain clean code by removing incorrect annotations that could be misleading.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new RemoveUnnecessaryOverrideVisitor(ignoreAnonymousClassMethods);
+    }
+
+    public static class RemoveUnnecessaryOverrideVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private final AnnotationMatcher OVERRIDE_ANNOTATION =
+                new AnnotationMatcher("@java.lang.Override");
+        private final Boolean ignoreAnonymousClassMethods;
+
+        public RemoveUnnecessaryOverrideVisitor(Boolean ignoreAnonymousClassMethods) {
+            this.ignoreAnonymousClassMethods = ignoreAnonymousClassMethods;
+        }
+
+        public RemoveUnnecessaryOverrideVisitor() {
+            this(false);
+        }
+
+        private Cursor getCursorToParentScope(Cursor cursor) {
+            return cursor.dropParentUntil(
+                    is -> is instanceof J.NewClass || is instanceof J.ClassDeclaration);
+        }
+
+        @Override
+        public J.MethodDeclaration visitMethodDeclaration(
+                J.MethodDeclaration method, ExecutionContext ctx) {
+            J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
+
+            if (!m.isConstructor()
+                    && service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)
+                    && !TypeUtils.isOverride(m.getMethodType())
+                    && !(Boolean.TRUE.equals(ignoreAnonymousClassMethods)
+                    && getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
            if (!m.isConstructor() &&
                    service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION) &&
                    !TypeUtils.isOverride(m.getMethodType()) &&
                    !(Boolean.TRUE.equals(ignoreAnonymousClassMethods) &&
                    getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) {
```
created_at: 2025-03-31T15:21:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,111 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeUtils;
+
+public class RemoveUnnecessarySuperCalls extends Recipe {
+
+
+    @Override
+    public String getDisplayName() {
+        return "Remove `super` calls when the class does not extend another class";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes calls to `super(...)` or `super.someMethod(...)` if the class does not have a real superclass besides `java.lang.Object`.";
+    }
+
+    @Override
+    public JavaIsoVisitor<ExecutionContext> getVisitor() {
+        return new RemoveUnnecessarySuperCallsVisitor();
+    }
+
+    public static class RemoveUnnecessarySuperCallsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static final String SUPER = "super";
+
+        @Override
+        public J.@Nullable MethodInvocation visitMethodInvocation(
+                J.MethodInvocation method, ExecutionContext ctx) {
+            J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
+
+            if (isSuperCall(mi)) {
+                J.ClassDeclaration classDecl = getCursor().firstEnclosing(J.ClassDeclaration.class);
+                if (classDecl != null) {
+                    boolean hasRealParent = hasRealSuperclass(classDecl);
+
+                    if (!hasRealParent) {
+                        return null;
+                    }
+
+                    boolean isOverride =
+                            mi.getMethodType() != null && TypeUtils.isOverride(mi.getMethodType());
+                    if (!isOverride) {
+                        return null;
+                    }
+                }
+            }
+            return mi;
+        }
+
+        /**
+         * Checks if this is a call on `super`, like `super(...)` or `super.someMethod(...)`.
+         */
+        private boolean isSuperCall(J.MethodInvocation mi) {
+            // e.g. super.method()
+            if (mi.getSelect() instanceof J.Identifier) {
+                return SUPER.equals(((J.Identifier) mi.getSelect()).getSimpleName());
+            }
+            // e.g. constructor call "super(...)"
+            return mi.getSelect() == null && SUPER.equals(mi.getSimpleName());
+        }
+
+        /**
+         * Checks if the class extends some real superclass other than `java.lang.Object`.
+         */
+        private boolean hasRealSuperclass(J.ClassDeclaration classDecl) {
+            if (classDecl.getExtends() == null) {
+                return false;
+            }
+            
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCalls.java
user: login: github-actions[bot]
body: ```suggestion

```
created_at: 2025-03-31T15:21:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,229 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method.util;
+
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class MethodStubCreator {
+
+    public static String buildThrowsClause(List<JavaType> thrownExceptions) {
+        if (thrownExceptions == null || thrownExceptions.isEmpty()) {
+            return "";
+        }
+
+        List<String> exceptionNames = new ArrayList<>();
+        for (JavaType exception : thrownExceptions) {
+            String exceptionName = buildTypeName(exception);
+            // Skip if we got the fallback type
+            if (!exceptionName.equals("Object")) {
+                exceptionNames.add(exceptionName);
+            }
+        }
+
+        return exceptionNames.isEmpty() ? "" : " throws " + String.join(", ", exceptionNames);
+    }
+
+    private static void collectGenericTypeVariables(
+            JavaType type, List<JavaType.GenericTypeVariable> vars) {
+        if (type == null) {
+            return;
+        }
+        if (type instanceof JavaType.GenericTypeVariable) {
+            JavaType.GenericTypeVariable gtv = (JavaType.GenericTypeVariable) type;
+            // Only add if we haven't already (or use a Set)
+            if (!vars.contains(gtv)) {
+                vars.add(gtv);
+            }
+            // GTV may have bounds, so check them too
+            if (gtv.getBounds() != null) {
+                for (JavaType bound : gtv.getBounds()) {
+                    collectGenericTypeVariables(bound, vars);
+                }
+            }
+        } else if (type instanceof JavaType.Parameterized) {
+            JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
+            // Recurse into type parameters
+            if (parameterized.getTypeParameters() != null) {
+                for (JavaType tp : parameterized.getTypeParameters()) {
+                    collectGenericTypeVariables(tp, vars);
+                }
+            }
+        } else if (type instanceof JavaType.Array) {
+            // Recurse into the element type
+            JavaType.Array arr = (JavaType.Array) type;
+            collectGenericTypeVariables(arr.getElemType(), vars);
+        }
+        // For FullyQualified, Primitive, etc. do nothing special
+    }
+
+    private static String buildMethodTypeParametersFromCollected(
+            List<JavaType.GenericTypeVariable> generics) {
+        if (generics.isEmpty()) {
+            return "";
+        }
+        List<String> parts = new ArrayList<>();
+        for (JavaType.GenericTypeVariable gtv : generics) {
+            String name = gtv.getName();
+            List<JavaType> bounds = gtv.getBounds();
+            if (bounds == null || bounds.isEmpty()) {
+                // No bounds
+                parts.add(name);
+            } else {
+                // e.g. "T extends java.lang.Comparable & SomeOtherBound"
+                String boundsStr =
+                        bounds.stream()
+                                .map(MethodStubCreator::buildTypeName)
+                                .collect(Collectors.joining(" & "));
+                parts.add(name + " extends " + boundsStr);
+            }
+        }
+        return "<" + String.join(", ", parts) + ">";
+    }
+
+    public static String buildMethodStub(JavaType.Method methodType) {
+        // 1) Collect all GenericTypeVariable from return + param types
+        List<JavaType.GenericTypeVariable> collectedVars = new ArrayList<>();
+
+        // Return type
+        collectGenericTypeVariables(methodType.getReturnType(), collectedVars);
+
+        // Parameter types
+        List<JavaType> paramTypes = methodType.getParameterTypes();
+        if (paramTypes != null) {
+            for (JavaType pt : paramTypes) {
+                collectGenericTypeVariables(pt, collectedVars);
+            }
+        }
+
+        // 2) Build e.g. "<T, U extends Something>"
+        String methodTypeParameters = buildMethodTypeParametersFromCollected(collectedVars);
+
+        // 3) Build normal method signature
+        String paramsSignature =
+                buildParameters(methodType.getParameterTypes(), methodType.getParameterNames());
+        String returnTypeName = buildReturnType(methodType.getReturnType());
+        String throwsClause = buildThrowsClause(methodType.getThrownExceptions());
+        String methodName = methodType.getName();
+
+        String methodTypeParametersTrimmed = methodTypeParameters.trim();
+        String maybeSpace = methodTypeParametersTrimmed.isEmpty() ? "" : " ";
+
+        return String.format(
+                "@Override%npublic %s%s%s %s(%s)%s {%n    throw new UnsupportedOperationException();%n}",
+                methodTypeParametersTrimmed,
+                maybeSpace,
+                returnTypeName,
+                methodName,
+                paramsSignature,
+                throwsClause);
+    }
+
+    public static String buildParameters(List<JavaType> parameterTypes, List<String> parameterNames) {
+        if (parameterTypes == null || parameterTypes.isEmpty()) {
+            return "";
+        }
+
+        List<String> params = new ArrayList<>();
+        for (int i = 0; i < parameterTypes.size(); i++) {
+            JavaType paramType = parameterTypes.get(i);
+            String paramName = getParameterName(parameterNames, i);
+            String paramTypeName = buildTypeName(paramType);
+            params.add(paramTypeName + " " + paramName);
+        }
+        return String.join(", ", params);
+    }
+
+    private static String getParameterName(List<String> parameterNames, int index) {
+        if (parameterNames != null
+                && index < parameterNames.size()
+                && parameterNames.get(index) != null
+                && !parameterNames.get(index).trim().isEmpty()) {
path: src/main/java/org/openrewrite/java/dropwizard/method/util/MethodStubCreator.java
user: login: github-actions[bot]
body: ```suggestion
        if (parameterNames != null &&
                index < parameterNames.size() &&
                parameterNames.get(index) != null &&
                !parameterNames.get(index).trim().isEmpty()) {
```
created_at: 2025-03-31T15:21:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,254 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class AddMissingAbstractMethodsTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new AddMissingAbstractMethods())
+          .parser(
+            JavaParser.fromJavaVersion()
+              .classpath(JavaParser.runtimeClasspath())
+              .logCompilationWarningsAndErrors(false));
+    }
+
+    @Test
+    void implementsAbstractMethod() {
+        rewriteRun(
+          java(
+            """
+              abstract class AbstractParent {
+                  abstract void doSomething(String input);
+              }
+              """),
+          java(
+            """
+              class Child extends AbstractParent {
+              }
+              """,
+            """
+              class Child extends AbstractParent {
+                  @Override
+                  public void doSomething(java.lang.String input) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsInterfaceMethod() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void processData(int value);
+              }
+              """),
+          java(
+            """
+              class Implementation implements MyInterface {
+              }
+              """,
+            """
+              class Implementation implements MyInterface {
+                  @Override
+                  public void processData(int value) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsMultipleInterfaces() {
+        rewriteRun(
+          java(
+            """
+              interface FirstInterface {
+                  void methodOne();
+              }
+              """),
+          java(
+            """
+              interface SecondInterface {
+                  void methodTwo(String input);
+              }
+              """),
+          java(
+            """
+              class Implementation implements FirstInterface, SecondInterface {
+              }
+              """,
+            """
+              class Implementation implements FirstInterface, SecondInterface {
+                  @Override
+                  public void methodOne() {
+                      throw new UnsupportedOperationException();
+                  }
+
+                  @Override
+                  public void methodTwo(java.lang.String input) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void doesNotDuplicateExistingImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void process();
+              }
+              """),
+          java(
+            """
+              class Implementation implements MyInterface {
+                  @Override
+                  public void process() {
+                      System.out.println("Processing");
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsAbstractMethodWithComplexParameters() {
+        rewriteRun(
+          java(
+            """
+              import java.util.List;
+              import java.util.Map;
+
+              abstract class ComplexParent {
+                  abstract <T> List<T> transform(Map<String, T> input, Class<T> type);
+              }
+              """),
+          java(
+            """
+              class Child extends ComplexParent {
+              }
+              """,
+            """
+              class Child extends ComplexParent {
+                  @Override
+                  public <T> java.util.List<T> transform(java.util.Map<java.lang.String, T> input, java.lang.Class<T> type) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void shouldNotDuplicateImplementedMethods() {
+        rewriteRun(
+          java(
+            """
+              abstract class AbstractParent {
+                  public abstract String getName();
+              }
+              """),
+          java(
+            """
+              class Child extends AbstractParent {
+                  private String name;
+
+                  public String getName() {
+                      return name;
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void shouldNotAddAnyDefaultOrObjectMethods() {
+        rewriteRun(
+          java(
+            """
+              import java.security.Principal;
+              import java.util.Set;
+
+              public class User implements Principal {
+                  private final String name;
+
+                  private final Set<String> roles;
+
+                  public User(String name) {
+                      this.name = name;
+                      this.roles = null;
+                  }
+
+                  public User(String name, Set<String> roles) {
+                      this.name = name;
+                      this.roles = roles;
+                  }
+
+                  public String getName() {
+                      return name;
+                  }
+
+                  public int getId() {
+                      return (int) (Math.random() * 100);
+                  }
+
+                  public Set<String> getRoles() {
+                      return roles;
+                  }
+              }
+              """));
+    }
+
+    @DocumentExample
+    @Test
+    void shouldTransformCommandClass() {
+        rewriteRun(
+          spec ->
+            spec.recipes(new AddMissingAbstractMethods())
+              .parser(
+                JavaParser.fromJavaVersion()
+                  .classpath(JavaParser.runtimeClasspath())
+                  .logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+              }
+              """,
+            """
+                import org.springframework.boot.CommandLineRunner;
+
+                public class RenderCommand implements CommandLineRunner {
+                    @Override
+                    public void run(java.lang.String[] args) throws java.lang.Exception {
+                        throw new UnsupportedOperationException();
+                    }
+                }
path: src/test/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethodsTest.java
user: login: github-actions[bot]
body: ```suggestion
              import org.springframework.boot.CommandLineRunner;

              public class RenderCommand implements CommandLineRunner {
                  @Override
                  public void run(java.lang.String[] args) throws java.lang.Exception {
                      throw new UnsupportedOperationException();
                  }
              }
```
created_at: 2025-03-31T15:21:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,299 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """));
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(
+              JavaParser.fromJavaVersion()
+                .classpath(JavaParser.runtimeClasspath())
+                .logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """));
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+                  class TestClass implements MyInterface {
+                      @Override
+                      public void correctMethod() {}
+
+
+                      public void wrongMethod() {
+                      }
+
+                      @Override
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              class TestClass implements MyInterface {
                  @Override
                  public void correctMethod() {}
                  public void wrongMethod() {

                  @Override
                  public void anotherMethod() {}
              }
```
created_at: 2025-03-31T15:21:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
                        if (!bounds.isEmpty() &&
                                !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
```
created_at: 2025-04-01T05:07:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
+                            return false;
+                        }
+                    }
+                } else if (!isAssignableTo(existingParam, candidateParam)) {
+                    return false;
+                }
+            }
+
+            // Check return type
+            JavaType existingReturn = existing.getReturnType();
+            JavaType candidateReturn = candidate.getReturnType();
+
+            if (existingReturn instanceof JavaType.Parameterized
+                    && candidateReturn instanceof JavaType.Parameterized) {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
            if (existingReturn instanceof JavaType.Parameterized &&
                    candidateReturn instanceof JavaType.Parameterized) {
```
created_at: 2025-04-01T05:07:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
+                            return false;
+                        }
+                    }
+                } else if (!isAssignableTo(existingParam, candidateParam)) {
+                    return false;
+                }
+            }
+
+            // Check return type
+            JavaType existingReturn = existing.getReturnType();
+            JavaType candidateReturn = candidate.getReturnType();
+
+            if (existingReturn instanceof JavaType.Parameterized
+                    && candidateReturn instanceof JavaType.Parameterized) {
+                JavaType.Parameterized existingParamReturn = (JavaType.Parameterized) existingReturn;
+                JavaType.Parameterized candidateParamReturn = (JavaType.Parameterized) candidateReturn;
+
+                // Compare the raw types (e.g., Optional)
+                if (!isAssignableTo(existingParamReturn.getType(), candidateParamReturn.getType())) {
+                    return false;
+                }
+
+                // Compare type arguments
+                List<JavaType> existingTypeArgs = existingParamReturn.getTypeParameters();
+                List<JavaType> candidateTypeArgs = candidateParamReturn.getTypeParameters();
+
+                if (existingTypeArgs.size() != candidateTypeArgs.size()) {
+                    return false;
+                }
+
+                for (int i = 0; i < existingTypeArgs.size(); i++) {
+                    JavaType existingArg = existingTypeArgs.get(i);
+                    JavaType candidateArg = candidateTypeArgs.get(i);
+
+                    if (candidateArg instanceof JavaType.GenericTypeVariable) {
+                        String genericName = ((JavaType.GenericTypeVariable) candidateArg).getName();
+                        if (typeMapping.containsKey(genericName)) {
+                            JavaType mappedType = typeMapping.get(genericName);
+                            if (!isAssignableTo(existingArg, mappedType)) {
+                                return false;
+                            }
+                        }
+                    } else if (!isAssignableTo(existingArg, candidateArg)) {
+                        return false;
+                    }
+                }
+            } else return isAssignableTo(existingReturn, candidateReturn);
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
            } else {
                return isAssignableTo(existingReturn, candidateReturn);
            }
```
created_at: 2025-04-01T05:07:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,67 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.tree.JavaType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByPackage extends Recipe {
+
+    @Option(displayName = "Package to match",
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackage.java
user: login: github-actions[bot]
body: ```suggestion
    @Option(example = "TODO Provide a usage example for the docs", displayName = "Package to match",
```
created_at: 2025-04-01T05:07:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,64 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.tree.JavaType;
+
+import static org.openrewrite.java.tree.TypeUtils.isOfClassType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByType extends Recipe {
+
+    @Option(displayName = "Fully qualified name of the superclass to remove",
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByType.java
user: login: github-actions[bot]
body: ```suggestion
    @Option(example = "TODO Provide a usage example for the docs", displayName = "Fully qualified name of the superclass to remove",
```
created_at: 2025-04-01T05:07:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,75 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.nonNull;
+
+public abstract class RemoveSuperTypeVisitor extends JavaIsoVisitor<ExecutionContext> {
+
+    protected abstract boolean shouldRemoveType(JavaType type);
+
+    @Override
+    public J.ClassDeclaration visitClassDeclaration(
+            J.ClassDeclaration classDecl, ExecutionContext ctx) {
+        J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+        JavaType.FullyQualified fqn =
+                TypeUtils.asFullyQualified(JavaType.buildType("java.lang.Object"));
+        boolean modified = false;
+
+        // Handle extends clause
+        if (nonNull(cd.getExtends())
+                && nonNull(cd.getExtends().getType())
+                && shouldRemoveType(cd.getExtends().getType())) {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeVisitor.java
user: login: github-actions[bot]
body: ```suggestion
        if (nonNull(cd.getExtends()) &&
                nonNull(cd.getExtends().getType()) &&
                shouldRemoveType(cd.getExtends().getType())) {
```
created_at: 2025-04-01T05:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
import lombok.Value;
import org.jspecify.annotations.Nullable;
```
created_at: 2025-04-01T05:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
import java.util.ArrayList;
```
created_at: 2025-04-01T05:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
+
+    @Option(
+            displayName = "Ignore methods in anonymous classes",
+            description = "When enabled, ignore @Override annotations on methods in anonymous classes.",
+            required = false)
+    @Nullable
+    Boolean ignoreAnonymousClassMethods;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove unnecessary `@Override` annotations";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes `@Override` annotations from methods that don't actually override or implement any method. "
+                + "This helps maintain clean code by removing incorrect annotations that could be misleading.";
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
        return "Removes `@Override` annotations from methods that don't actually override or implement any method. " +
                "This helps maintain clean code by removing incorrect annotations that could be misleading.";
```
created_at: 2025-04-01T05:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
+
+    @Option(
+            displayName = "Ignore methods in anonymous classes",
+            description = "When enabled, ignore @Override annotations on methods in anonymous classes.",
+            required = false)
+    @Nullable
+    Boolean ignoreAnonymousClassMethods;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove unnecessary `@Override` annotations";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes `@Override` annotations from methods that don't actually override or implement any method. "
+                + "This helps maintain clean code by removing incorrect annotations that could be misleading.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new RemoveUnnecessaryOverrideVisitor(ignoreAnonymousClassMethods);
+    }
+
+    public static class RemoveUnnecessaryOverrideVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private final AnnotationMatcher OVERRIDE_ANNOTATION =
+                new AnnotationMatcher("@java.lang.Override");
+        private final Boolean ignoreAnonymousClassMethods;
+
+        public RemoveUnnecessaryOverrideVisitor(Boolean ignoreAnonymousClassMethods) {
+            this.ignoreAnonymousClassMethods = ignoreAnonymousClassMethods;
+        }
+
+        public RemoveUnnecessaryOverrideVisitor() {
+            this(false);
+        }
+
+        private Cursor getCursorToParentScope(Cursor cursor) {
+            return cursor.dropParentUntil(
+                    is -> is instanceof J.NewClass || is instanceof J.ClassDeclaration);
+        }
+
+        @Override
+        public J.MethodDeclaration visitMethodDeclaration(
+                J.MethodDeclaration method, ExecutionContext ctx) {
+            J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
+
+            if (!m.isConstructor()
+                    && service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)
+                    && !TypeUtils.isOverride(m.getMethodType())
+                    && !(Boolean.TRUE.equals(ignoreAnonymousClassMethods)
+                    && getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
            if (!m.isConstructor() &&
                    service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION) &&
                    !TypeUtils.isOverride(m.getMethodType()) &&
                    !(Boolean.TRUE.equals(ignoreAnonymousClassMethods) &&
                    getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) {
```
created_at: 2025-04-01T05:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,111 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeUtils;
+
+public class RemoveUnnecessarySuperCalls extends Recipe {
+
+
+    @Override
+    public String getDisplayName() {
+        return "Remove `super` calls when the class does not extend another class";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes calls to `super(...)` or `super.someMethod(...)` if the class does not have a real superclass besides `java.lang.Object`.";
+    }
+
+    @Override
+    public JavaIsoVisitor<ExecutionContext> getVisitor() {
+        return new RemoveUnnecessarySuperCallsVisitor();
+    }
+
+    public static class RemoveUnnecessarySuperCallsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static final String SUPER = "super";
+
+        @Override
+        public J.@Nullable MethodInvocation visitMethodInvocation(
+                J.MethodInvocation method, ExecutionContext ctx) {
+            J.MethodInvocation mi = super.visitMethodInvocation(method, ctx);
+
+            if (isSuperCall(mi)) {
+                J.ClassDeclaration classDecl = getCursor().firstEnclosing(J.ClassDeclaration.class);
+                if (classDecl != null) {
+                    boolean hasRealParent = hasRealSuperclass(classDecl);
+
+                    if (!hasRealParent) {
+                        return null;
+                    }
+
+                    boolean isOverride =
+                            mi.getMethodType() != null && TypeUtils.isOverride(mi.getMethodType());
+                    if (!isOverride) {
+                        return null;
+                    }
+                }
+            }
+            return mi;
+        }
+
+        /**
+         * Checks if this is a call on `super`, like `super(...)` or `super.someMethod(...)`.
+         */
+        private boolean isSuperCall(J.MethodInvocation mi) {
+            // e.g. super.method()
+            if (mi.getSelect() instanceof J.Identifier) {
+                return SUPER.equals(((J.Identifier) mi.getSelect()).getSimpleName());
+            }
+            // e.g. constructor call "super(...)"
+            return mi.getSelect() == null && SUPER.equals(mi.getSimpleName());
+        }
+
+        /**
+         * Checks if the class extends some real superclass other than `java.lang.Object`.
+         */
+        private boolean hasRealSuperclass(J.ClassDeclaration classDecl) {
+            if (classDecl.getExtends() == null) {
+                return false;
+            }
+            
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessarySuperCalls.java
user: login: github-actions[bot]
body: ```suggestion

```
created_at: 2025-04-01T05:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,229 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method.util;
+
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class MethodStubCreator {
+
+    public static String buildThrowsClause(List<JavaType> thrownExceptions) {
+        if (thrownExceptions == null || thrownExceptions.isEmpty()) {
+            return "";
+        }
+
+        List<String> exceptionNames = new ArrayList<>();
+        for (JavaType exception : thrownExceptions) {
+            String exceptionName = buildTypeName(exception);
+            // Skip if we got the fallback type
+            if (!exceptionName.equals("Object")) {
+                exceptionNames.add(exceptionName);
+            }
+        }
+
+        return exceptionNames.isEmpty() ? "" : " throws " + String.join(", ", exceptionNames);
+    }
+
+    private static void collectGenericTypeVariables(
+            JavaType type, List<JavaType.GenericTypeVariable> vars) {
+        if (type == null) {
+            return;
+        }
+        if (type instanceof JavaType.GenericTypeVariable) {
+            JavaType.GenericTypeVariable gtv = (JavaType.GenericTypeVariable) type;
+            // Only add if we haven't already (or use a Set)
+            if (!vars.contains(gtv)) {
+                vars.add(gtv);
+            }
+            // GTV may have bounds, so check them too
+            if (gtv.getBounds() != null) {
+                for (JavaType bound : gtv.getBounds()) {
+                    collectGenericTypeVariables(bound, vars);
+                }
+            }
+        } else if (type instanceof JavaType.Parameterized) {
+            JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
+            // Recurse into type parameters
+            if (parameterized.getTypeParameters() != null) {
+                for (JavaType tp : parameterized.getTypeParameters()) {
+                    collectGenericTypeVariables(tp, vars);
+                }
+            }
+        } else if (type instanceof JavaType.Array) {
+            // Recurse into the element type
+            JavaType.Array arr = (JavaType.Array) type;
+            collectGenericTypeVariables(arr.getElemType(), vars);
+        }
+        // For FullyQualified, Primitive, etc. do nothing special
+    }
+
+    private static String buildMethodTypeParametersFromCollected(
+            List<JavaType.GenericTypeVariable> generics) {
+        if (generics.isEmpty()) {
+            return "";
+        }
+        List<String> parts = new ArrayList<>();
+        for (JavaType.GenericTypeVariable gtv : generics) {
+            String name = gtv.getName();
+            List<JavaType> bounds = gtv.getBounds();
+            if (bounds == null || bounds.isEmpty()) {
+                // No bounds
+                parts.add(name);
+            } else {
+                // e.g. "T extends java.lang.Comparable & SomeOtherBound"
+                String boundsStr =
+                        bounds.stream()
+                                .map(MethodStubCreator::buildTypeName)
+                                .collect(Collectors.joining(" & "));
+                parts.add(name + " extends " + boundsStr);
+            }
+        }
+        return "<" + String.join(", ", parts) + ">";
+    }
+
+    public static String buildMethodStub(JavaType.Method methodType) {
+        // 1) Collect all GenericTypeVariable from return + param types
+        List<JavaType.GenericTypeVariable> collectedVars = new ArrayList<>();
+
+        // Return type
+        collectGenericTypeVariables(methodType.getReturnType(), collectedVars);
+
+        // Parameter types
+        List<JavaType> paramTypes = methodType.getParameterTypes();
+        if (paramTypes != null) {
+            for (JavaType pt : paramTypes) {
+                collectGenericTypeVariables(pt, collectedVars);
+            }
+        }
+
+        // 2) Build e.g. "<T, U extends Something>"
+        String methodTypeParameters = buildMethodTypeParametersFromCollected(collectedVars);
+
+        // 3) Build normal method signature
+        String paramsSignature =
+                buildParameters(methodType.getParameterTypes(), methodType.getParameterNames());
+        String returnTypeName = buildReturnType(methodType.getReturnType());
+        String throwsClause = buildThrowsClause(methodType.getThrownExceptions());
+        String methodName = methodType.getName();
+
+        String methodTypeParametersTrimmed = methodTypeParameters.trim();
+        String maybeSpace = methodTypeParametersTrimmed.isEmpty() ? "" : " ";
+
+        return String.format(
+                "@Override%npublic %s%s%s %s(%s)%s {%n    throw new UnsupportedOperationException();%n}",
+                methodTypeParametersTrimmed,
+                maybeSpace,
+                returnTypeName,
+                methodName,
+                paramsSignature,
+                throwsClause);
+    }
+
+    public static String buildParameters(List<JavaType> parameterTypes, List<String> parameterNames) {
+        if (parameterTypes == null || parameterTypes.isEmpty()) {
+            return "";
+        }
+
+        List<String> params = new ArrayList<>();
+        for (int i = 0; i < parameterTypes.size(); i++) {
+            JavaType paramType = parameterTypes.get(i);
+            String paramName = getParameterName(parameterNames, i);
+            String paramTypeName = buildTypeName(paramType);
+            params.add(paramTypeName + " " + paramName);
+        }
+        return String.join(", ", params);
+    }
+
+    private static String getParameterName(List<String> parameterNames, int index) {
+        if (parameterNames != null
+                && index < parameterNames.size()
+                && parameterNames.get(index) != null
+                && !parameterNames.get(index).trim().isEmpty()) {
path: src/main/java/org/openrewrite/java/dropwizard/method/util/MethodStubCreator.java
user: login: github-actions[bot]
body: ```suggestion
        if (parameterNames != null &&
                index < parameterNames.size() &&
                parameterNames.get(index) != null &&
                !parameterNames.get(index).trim().isEmpty()) {
```
created_at: 2025-04-01T05:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,254 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class AddMissingAbstractMethodsTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new AddMissingAbstractMethods())
+          .parser(
+            JavaParser.fromJavaVersion()
+              .classpath(JavaParser.runtimeClasspath())
+              .logCompilationWarningsAndErrors(false));
+    }
+
+    @Test
+    void implementsAbstractMethod() {
+        rewriteRun(
+          java(
+            """
+              abstract class AbstractParent {
+                  abstract void doSomething(String input);
+              }
+              """),
+          java(
+            """
+              class Child extends AbstractParent {
+              }
+              """,
+            """
+              class Child extends AbstractParent {
+                  @Override
+                  public void doSomething(java.lang.String input) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsInterfaceMethod() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void processData(int value);
+              }
+              """),
+          java(
+            """
+              class Implementation implements MyInterface {
+              }
+              """,
+            """
+              class Implementation implements MyInterface {
+                  @Override
+                  public void processData(int value) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsMultipleInterfaces() {
+        rewriteRun(
+          java(
+            """
+              interface FirstInterface {
+                  void methodOne();
+              }
+              """),
+          java(
+            """
+              interface SecondInterface {
+                  void methodTwo(String input);
+              }
+              """),
+          java(
+            """
+              class Implementation implements FirstInterface, SecondInterface {
+              }
+              """,
+            """
+              class Implementation implements FirstInterface, SecondInterface {
+                  @Override
+                  public void methodOne() {
+                      throw new UnsupportedOperationException();
+                  }
+
+                  @Override
+                  public void methodTwo(java.lang.String input) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void doesNotDuplicateExistingImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void process();
+              }
+              """),
+          java(
+            """
+              class Implementation implements MyInterface {
+                  @Override
+                  public void process() {
+                      System.out.println("Processing");
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsAbstractMethodWithComplexParameters() {
+        rewriteRun(
+          java(
+            """
+              import java.util.List;
+              import java.util.Map;
+
+              abstract class ComplexParent {
+                  abstract <T> List<T> transform(Map<String, T> input, Class<T> type);
+              }
+              """),
+          java(
+            """
+              class Child extends ComplexParent {
+              }
+              """,
+            """
+              class Child extends ComplexParent {
+                  @Override
+                  public <T> java.util.List<T> transform(java.util.Map<java.lang.String, T> input, java.lang.Class<T> type) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void shouldNotDuplicateImplementedMethods() {
+        rewriteRun(
+          java(
+            """
+              abstract class AbstractParent {
+                  public abstract String getName();
+              }
+              """),
+          java(
+            """
+              class Child extends AbstractParent {
+                  private String name;
+
+                  public String getName() {
+                      return name;
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void shouldNotAddAnyDefaultOrObjectMethods() {
+        rewriteRun(
+          java(
+            """
+              import java.security.Principal;
+              import java.util.Set;
+
+              public class User implements Principal {
+                  private final String name;
+
+                  private final Set<String> roles;
+
+                  public User(String name) {
+                      this.name = name;
+                      this.roles = null;
+                  }
+
+                  public User(String name, Set<String> roles) {
+                      this.name = name;
+                      this.roles = roles;
+                  }
+
+                  public String getName() {
+                      return name;
+                  }
+
+                  public int getId() {
+                      return (int) (Math.random() * 100);
+                  }
+
+                  public Set<String> getRoles() {
+                      return roles;
+                  }
+              }
+              """));
+    }
+
+    @DocumentExample
+    @Test
+    void shouldTransformCommandClass() {
+        rewriteRun(
+          spec ->
+            spec.recipes(new AddMissingAbstractMethods())
+              .parser(
+                JavaParser.fromJavaVersion()
+                  .classpath("")
+                  .logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+              }
+              """,
+            """
+                import org.springframework.boot.CommandLineRunner;
+
+                public class RenderCommand implements CommandLineRunner {
+                    @Override
+                    public void run(java.lang.String[] args) throws java.lang.Exception {
+                        throw new UnsupportedOperationException();
+                    }
+                }
path: src/test/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethodsTest.java
user: login: github-actions[bot]
body: ```suggestion
              import org.springframework.boot.CommandLineRunner;

              public class RenderCommand implements CommandLineRunner {
                  @Override
                  public void run(java.lang.String[] args) throws java.lang.Exception {
                      throw new UnsupportedOperationException();
                  }
              }
```
created_at: 2025-04-01T05:07:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,294 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
```
created_at: 2025-04-01T05:07:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,294 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
    @Test
```
created_at: 2025-04-01T05:07:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,294 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """));
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+                  class TestClass implements MyInterface {
+                      @Override
+                      public void correctMethod() {}
+
+
+                      public void wrongMethod() {
+                      }
+
+                      @Override
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              class TestClass implements MyInterface {
                  @Override
                  public void correctMethod() {}
                  public void wrongMethod() {

                  @Override
                  public void anotherMethod() {}
              }
```
created_at: 2025-04-01T05:07:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
                        if (!bounds.isEmpty() &&
                                !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
```
created_at: 2025-04-01T05:10:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
+                            return false;
+                        }
+                    }
+                } else if (!isAssignableTo(existingParam, candidateParam)) {
+                    return false;
+                }
+            }
+
+            // Check return type
+            JavaType existingReturn = existing.getReturnType();
+            JavaType candidateReturn = candidate.getReturnType();
+
+            if (existingReturn instanceof JavaType.Parameterized
+                    && candidateReturn instanceof JavaType.Parameterized) {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
            if (existingReturn instanceof JavaType.Parameterized &&
                    candidateReturn instanceof JavaType.Parameterized) {
```
created_at: 2025-04-01T05:10:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,357 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
+    @Override
+    public String getDisplayName() {
+        return "Add missing abstract methods";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Implements missing abstract methods from superclasses and interfaces.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new AddMissingMethodsVisitor();
+    }
+
+    public static class AddMissingMethodsVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private static List<JavaType.Method> getAbstractMethods(JavaType.FullyQualified implType) {
+            if (implType.getMethods() == null) {
+                return Collections.emptyList();
+            }
+
+            return implType.getMethods().stream()
+                    .filter(m -> m.hasFlags(Abstract))
+                    .filter(m -> !m.hasFlags(Default))
+                    .collect(Collectors.toList());
+        }
+
+        private static Set<String> getObjectClassMethods() {
+            Set<String> objectMethodNames = new HashSet<>();
+            objectMethodNames.add("equals");
+            objectMethodNames.add("hashCode");
+            objectMethodNames.add("toString");
+            objectMethodNames.add("clone");
+            objectMethodNames.add("finalize");
+            objectMethodNames.add("getClass");
+            objectMethodNames.add("notify");
+            objectMethodNames.add("notifyAll");
+            objectMethodNames.add("wait");
+            return objectMethodNames;
+        }
+
+        @Override
+        public J.ClassDeclaration visitClassDeclaration(
+                J.ClassDeclaration classDecl, ExecutionContext ctx) {
+            J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+            if (isNull(cd.getType())) {
+                return cd;
+            }
+
+            List<JavaType.Method> abstractMethods = new ArrayList<>();
+
+            JavaType.FullyQualified extendsType =
+                    asFullyQualified(nonNull(cd.getExtends()) ? cd.getExtends().getType() : null);
+
+            if (nonNull(extendsType)) {
+                abstractMethods.addAll(getAbstractMethods(extendsType));
+            }
+
+            if (nonNull(cd.getImplements())) {
+                for (TypeTree impl : cd.getImplements()) {
+                    JavaType.FullyQualified implType = asFullyQualified(impl.getType());
+                    if (nonNull(implType)) {
+                        abstractMethods.addAll(getAbstractMethods(implType));
+                    }
+                }
+            }
+
+            List<JavaType.Method> missingMethods = findMissingAbstractMethods(cd, abstractMethods);
+
+            if (missingMethods.isEmpty()) {
+                return cd;
+            }
+
+            for (JavaType.Method missingMethod : missingMethods) {
+                cd = createMethod(cd, missingMethod);
+            }
+
+            return maybeAutoFormat(classDecl, cd, ctx);
+        }
+
+        public J.ClassDeclaration createMethod(J.ClassDeclaration cd, JavaType.Method methodType) {
+            if (methodType == null) {
+                return cd;
+            }
+
+            JavaTemplate template =
+                    JavaTemplate.builder(buildMethodStub(methodType)).contextSensitive().build();
+
+            return template.apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement());
+        }
+
+        private List<JavaType.Method> findMissingAbstractMethods(
+                J.ClassDeclaration classDecl, List<JavaType.Method> candidateAbstractMethods) {
+            JavaType.FullyQualified classType = classDecl.getType();
+            if (classType == null) {
+                return Collections.emptyList();
+            }
+
+            // Get all method declarations from the class body
+            List<J.MethodDeclaration> existingMethodDecls =
+                    classDecl.getBody().getStatements().stream()
+                            .filter(statement -> statement instanceof J.MethodDeclaration)
+                            .map(statement -> (J.MethodDeclaration) statement)
+                            .collect(Collectors.toList());
+
+            List<JavaType.Method> missing = new ArrayList<>();
+            Set<String> objectMethodNames = getObjectClassMethods();
+
+            for (JavaType.Method abstractMethod : candidateAbstractMethods) {
+                if (objectMethodNames.contains(abstractMethod.getName())) {
+                    continue;
+                }
+
+                boolean alreadyImplemented =
+                        existingMethodDecls.stream()
+                                .anyMatch(
+                                        methodDecl -> {
+                                            JavaType.Method methodType = methodDecl.getMethodType();
+                                            return methodType != null && signaturesMatch(methodType, abstractMethod);
+                                        });
+
+                if (!alreadyImplemented) {
+                    missing.add(abstractMethod);
+                }
+            }
+
+            return missing;
+        }
+
+        private boolean signaturesMatch(JavaType.Method existing, JavaType.Method candidate) {
+            if (!existing.getName().equals(candidate.getName())) {
+                return false;
+            }
+
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Then do a robust generic check
+            // We need the parent's JavaType.Parameterized if it's generic, e.g. the parent's
+            // "ComplexParent"
+            JavaType parentType = existing.getDeclaringType();
+            if (parentType instanceof JavaType.Parameterized) {
+                return methodMatchesGenericDefinition(
+                        existing, candidate, (JavaType.Parameterized) parentType);
+            } else {
+                // fallback: compare them in the simpler, old-fashioned way
+                return isAssignableTo(existing.getReturnType(), candidate.getReturnType());
+            }
+        }
+
+        private boolean areTypesCompatible(JavaType type1, JavaType type2) {
+            if (type1 == null || type2 == null) {
+                return type1 == type2;
+            }
+
+            // Get fully qualified names if possible
+            String name1 = getFullyQualifiedName(type1);
+            String name2 = getFullyQualifiedName(type2);
+
+            if (name1 != null && name2 != null) {
+                // Handle primitive type variations (e.g., int vs java.lang.Integer)
+                name1 = normalizePrimitiveType(name1);
+                name2 = normalizePrimitiveType(name2);
+                return name1.equals(name2);
+            }
+
+            // Handle parameterized types
+            if (type1 instanceof JavaType.Parameterized && type2 instanceof JavaType.Parameterized) {
+                return areParameterizedTypesCompatible(
+                        (JavaType.Parameterized) type1, (JavaType.Parameterized) type2);
+            }
+
+            // If we can't determine compatibility, err on the side of caution
+            return false;
+        }
+
+        private String getFullyQualifiedName(JavaType type) {
+            if (type instanceof JavaType.Primitive) {
+                return ((JavaType.Primitive) type).getKeyword();
+            }
+
+            JavaType.FullyQualified fq = TypeUtils.asFullyQualified(type);
+            return fq != null ? fq.getFullyQualifiedName() : null;
+        }
+
+        private String normalizePrimitiveType(String typeName) {
+            Map<String, String> primitiveToWrapper = new HashMap<>();
+            primitiveToWrapper.put("int", "java.lang.Integer");
+            primitiveToWrapper.put("long", "java.lang.Long");
+            primitiveToWrapper.put("boolean", "java.lang.Boolean");
+            primitiveToWrapper.put("byte", "java.lang.Byte");
+            primitiveToWrapper.put("short", "java.lang.Short");
+            primitiveToWrapper.put("char", "java.lang.Character");
+            primitiveToWrapper.put("float", "java.lang.Float");
+            primitiveToWrapper.put("double", "java.lang.Double");
+
+            return primitiveToWrapper.getOrDefault(typeName, typeName);
+        }
+
+        private boolean areParameterizedTypesCompatible(
+                JavaType.Parameterized type1, JavaType.Parameterized type2) {
+            // Check base type compatibility
+            if (!areTypesCompatible(type1.getType(), type2.getType())) {
+                return false;
+            }
+
+            // Check type parameters
+            List<JavaType> params1 = type1.getTypeParameters();
+            List<JavaType> params2 = type2.getTypeParameters();
+
+            if (params1.size() != params2.size()) {
+                return false;
+            }
+
+            for (int i = 0; i < params1.size(); i++) {
+                if (!areTypesCompatible(params1.get(i), params2.get(i))) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        private boolean methodMatchesGenericDefinition(
+                JavaType.Method existing, JavaType.Method candidate, JavaType.Parameterized declaringType) {
+
+            // Compare parameter counts
+            if (existing.getParameterTypes().size() != candidate.getParameterTypes().size()) {
+                return false;
+            }
+
+            // Get type mappings from the parameterized type
+            Map<JavaType, JavaType> typeMapping = new HashMap<>();
+            JavaType.FullyQualified rawType = declaringType.getType();
+            if (rawType instanceof JavaType.Class) {
+                JavaType.Class classType = (JavaType.Class) rawType;
+                List<JavaType> typeVarNames = classType.getTypeParameters();
+                List<JavaType> typeArgs = declaringType.getTypeParameters();
+
+                for (int i = 0; i < typeVarNames.size() && i < typeArgs.size(); i++) {
+                    typeMapping.put(typeVarNames.get(i), typeArgs.get(i));
+                }
+            }
+
+            // Check parameters
+            for (int i = 0; i < existing.getParameterTypes().size(); i++) {
+                JavaType existingParam = existing.getParameterTypes().get(i);
+                JavaType candidateParam = candidate.getParameterTypes().get(i);
+
+                if (candidateParam instanceof JavaType.GenericTypeVariable) {
+                    JavaType.GenericTypeVariable genericParam = (JavaType.GenericTypeVariable) candidateParam;
+                    String genericName = genericParam.getName();
+
+                    // Check if this generic parameter has a mapping
+                    if (typeMapping.containsKey(genericName)) {
+                        JavaType mappedType = typeMapping.get(genericName);
+                        if (!isAssignableTo(existingParam, mappedType)) {
+                            return false;
+                        }
+                    } else {
+                        // If no mapping exists, check bounds
+                        List<JavaType> bounds = genericParam.getBounds();
+                        if (!bounds.isEmpty()
+                                && !bounds.stream().allMatch(bound -> isAssignableTo(existingParam, bound))) {
+                            return false;
+                        }
+                    }
+                } else if (!isAssignableTo(existingParam, candidateParam)) {
+                    return false;
+                }
+            }
+
+            // Check return type
+            JavaType existingReturn = existing.getReturnType();
+            JavaType candidateReturn = candidate.getReturnType();
+
+            if (existingReturn instanceof JavaType.Parameterized
+                    && candidateReturn instanceof JavaType.Parameterized) {
+                JavaType.Parameterized existingParamReturn = (JavaType.Parameterized) existingReturn;
+                JavaType.Parameterized candidateParamReturn = (JavaType.Parameterized) candidateReturn;
+
+                // Compare the raw types (e.g., Optional)
+                if (!isAssignableTo(existingParamReturn.getType(), candidateParamReturn.getType())) {
+                    return false;
+                }
+
+                // Compare type arguments
+                List<JavaType> existingTypeArgs = existingParamReturn.getTypeParameters();
+                List<JavaType> candidateTypeArgs = candidateParamReturn.getTypeParameters();
+
+                if (existingTypeArgs.size() != candidateTypeArgs.size()) {
+                    return false;
+                }
+
+                for (int i = 0; i < existingTypeArgs.size(); i++) {
+                    JavaType existingArg = existingTypeArgs.get(i);
+                    JavaType candidateArg = candidateTypeArgs.get(i);
+
+                    if (candidateArg instanceof JavaType.GenericTypeVariable) {
+                        String genericName = ((JavaType.GenericTypeVariable) candidateArg).getName();
+                        if (typeMapping.containsKey(genericName)) {
+                            JavaType mappedType = typeMapping.get(genericName);
+                            if (!isAssignableTo(existingArg, mappedType)) {
+                                return false;
+                            }
+                        }
+                    } else if (!isAssignableTo(existingArg, candidateArg)) {
+                        return false;
+                    }
+                }
+            } else return isAssignableTo(existingReturn, candidateReturn);
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: github-actions[bot]
body: ```suggestion
            } else {
                return isAssignableTo(existingReturn, candidateReturn);
            }
```
created_at: 2025-04-01T05:10:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,75 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.nonNull;
+
+public abstract class RemoveSuperTypeVisitor extends JavaIsoVisitor<ExecutionContext> {
+
+    protected abstract boolean shouldRemoveType(JavaType type);
+
+    @Override
+    public J.ClassDeclaration visitClassDeclaration(
+            J.ClassDeclaration classDecl, ExecutionContext ctx) {
+        J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
+
+        JavaType.FullyQualified fqn =
+                TypeUtils.asFullyQualified(JavaType.buildType("java.lang.Object"));
+        boolean modified = false;
+
+        // Handle extends clause
+        if (nonNull(cd.getExtends())
+                && nonNull(cd.getExtends().getType())
+                && shouldRemoveType(cd.getExtends().getType())) {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeVisitor.java
user: login: github-actions[bot]
body: ```suggestion
        if (nonNull(cd.getExtends()) &&
                nonNull(cd.getExtends().getType()) &&
                shouldRemoveType(cd.getExtends().getType())) {
```
created_at: 2025-04-01T05:10:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
import lombok.Value;
import org.jspecify.annotations.Nullable;
```
created_at: 2025-04-01T05:10:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
import java.util.ArrayList;
```
created_at: 2025-04-01T05:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
+
+    @Option(
+            displayName = "Ignore methods in anonymous classes",
+            description = "When enabled, ignore @Override annotations on methods in anonymous classes.",
+            required = false)
+    @Nullable
+    Boolean ignoreAnonymousClassMethods;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove unnecessary `@Override` annotations";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes `@Override` annotations from methods that don't actually override or implement any method. "
+                + "This helps maintain clean code by removing incorrect annotations that could be misleading.";
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
        return "Removes `@Override` annotations from methods that don't actually override or implement any method. " +
                "This helps maintain clean code by removing incorrect annotations that could be misleading.";
```
created_at: 2025-04-01T05:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
+
+    @Option(
+            displayName = "Ignore methods in anonymous classes",
+            description = "When enabled, ignore @Override annotations on methods in anonymous classes.",
+            required = false)
+    @Nullable
+    Boolean ignoreAnonymousClassMethods;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove unnecessary `@Override` annotations";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes `@Override` annotations from methods that don't actually override or implement any method. "
+                + "This helps maintain clean code by removing incorrect annotations that could be misleading.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new RemoveUnnecessaryOverrideVisitor(ignoreAnonymousClassMethods);
+    }
+
+    public static class RemoveUnnecessaryOverrideVisitor extends JavaIsoVisitor<ExecutionContext> {
+        private final AnnotationMatcher OVERRIDE_ANNOTATION =
+                new AnnotationMatcher("@java.lang.Override");
+        private final Boolean ignoreAnonymousClassMethods;
+
+        public RemoveUnnecessaryOverrideVisitor(Boolean ignoreAnonymousClassMethods) {
+            this.ignoreAnonymousClassMethods = ignoreAnonymousClassMethods;
+        }
+
+        public RemoveUnnecessaryOverrideVisitor() {
+            this(false);
+        }
+
+        private Cursor getCursorToParentScope(Cursor cursor) {
+            return cursor.dropParentUntil(
+                    is -> is instanceof J.NewClass || is instanceof J.ClassDeclaration);
+        }
+
+        @Override
+        public J.MethodDeclaration visitMethodDeclaration(
+                J.MethodDeclaration method, ExecutionContext ctx) {
+            J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx);
+
+            if (!m.isConstructor()
+                    && service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)
+                    && !TypeUtils.isOverride(m.getMethodType())
+                    && !(Boolean.TRUE.equals(ignoreAnonymousClassMethods)
+                    && getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: github-actions[bot]
body: ```suggestion
            if (!m.isConstructor() &&
                    service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION) &&
                    !TypeUtils.isOverride(m.getMethodType()) &&
                    !(Boolean.TRUE.equals(ignoreAnonymousClassMethods) &&
                    getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) {
```
created_at: 2025-04-01T05:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,229 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method.util;
+
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class MethodStubCreator {
+
+    public static String buildThrowsClause(List<JavaType> thrownExceptions) {
+        if (thrownExceptions == null || thrownExceptions.isEmpty()) {
+            return "";
+        }
+
+        List<String> exceptionNames = new ArrayList<>();
+        for (JavaType exception : thrownExceptions) {
+            String exceptionName = buildTypeName(exception);
+            // Skip if we got the fallback type
+            if (!exceptionName.equals("Object")) {
+                exceptionNames.add(exceptionName);
+            }
+        }
+
+        return exceptionNames.isEmpty() ? "" : " throws " + String.join(", ", exceptionNames);
+    }
+
+    private static void collectGenericTypeVariables(
+            JavaType type, List<JavaType.GenericTypeVariable> vars) {
+        if (type == null) {
+            return;
+        }
+        if (type instanceof JavaType.GenericTypeVariable) {
+            JavaType.GenericTypeVariable gtv = (JavaType.GenericTypeVariable) type;
+            // Only add if we haven't already (or use a Set)
+            if (!vars.contains(gtv)) {
+                vars.add(gtv);
+            }
+            // GTV may have bounds, so check them too
+            if (gtv.getBounds() != null) {
+                for (JavaType bound : gtv.getBounds()) {
+                    collectGenericTypeVariables(bound, vars);
+                }
+            }
+        } else if (type instanceof JavaType.Parameterized) {
+            JavaType.Parameterized parameterized = (JavaType.Parameterized) type;
+            // Recurse into type parameters
+            if (parameterized.getTypeParameters() != null) {
+                for (JavaType tp : parameterized.getTypeParameters()) {
+                    collectGenericTypeVariables(tp, vars);
+                }
+            }
+        } else if (type instanceof JavaType.Array) {
+            // Recurse into the element type
+            JavaType.Array arr = (JavaType.Array) type;
+            collectGenericTypeVariables(arr.getElemType(), vars);
+        }
+        // For FullyQualified, Primitive, etc. do nothing special
+    }
+
+    private static String buildMethodTypeParametersFromCollected(
+            List<JavaType.GenericTypeVariable> generics) {
+        if (generics.isEmpty()) {
+            return "";
+        }
+        List<String> parts = new ArrayList<>();
+        for (JavaType.GenericTypeVariable gtv : generics) {
+            String name = gtv.getName();
+            List<JavaType> bounds = gtv.getBounds();
+            if (bounds == null || bounds.isEmpty()) {
+                // No bounds
+                parts.add(name);
+            } else {
+                // e.g. "T extends java.lang.Comparable & SomeOtherBound"
+                String boundsStr =
+                        bounds.stream()
+                                .map(MethodStubCreator::buildTypeName)
+                                .collect(Collectors.joining(" & "));
+                parts.add(name + " extends " + boundsStr);
+            }
+        }
+        return "<" + String.join(", ", parts) + ">";
+    }
+
+    public static String buildMethodStub(JavaType.Method methodType) {
+        // 1) Collect all GenericTypeVariable from return + param types
+        List<JavaType.GenericTypeVariable> collectedVars = new ArrayList<>();
+
+        // Return type
+        collectGenericTypeVariables(methodType.getReturnType(), collectedVars);
+
+        // Parameter types
+        List<JavaType> paramTypes = methodType.getParameterTypes();
+        if (paramTypes != null) {
+            for (JavaType pt : paramTypes) {
+                collectGenericTypeVariables(pt, collectedVars);
+            }
+        }
+
+        // 2) Build e.g. "<T, U extends Something>"
+        String methodTypeParameters = buildMethodTypeParametersFromCollected(collectedVars);
+
+        // 3) Build normal method signature
+        String paramsSignature =
+                buildParameters(methodType.getParameterTypes(), methodType.getParameterNames());
+        String returnTypeName = buildReturnType(methodType.getReturnType());
+        String throwsClause = buildThrowsClause(methodType.getThrownExceptions());
+        String methodName = methodType.getName();
+
+        String methodTypeParametersTrimmed = methodTypeParameters.trim();
+        String maybeSpace = methodTypeParametersTrimmed.isEmpty() ? "" : " ";
+
+        return String.format(
+                "@Override%npublic %s%s%s %s(%s)%s {%n    throw new UnsupportedOperationException();%n}",
+                methodTypeParametersTrimmed,
+                maybeSpace,
+                returnTypeName,
+                methodName,
+                paramsSignature,
+                throwsClause);
+    }
+
+    public static String buildParameters(List<JavaType> parameterTypes, List<String> parameterNames) {
+        if (parameterTypes == null || parameterTypes.isEmpty()) {
+            return "";
+        }
+
+        List<String> params = new ArrayList<>();
+        for (int i = 0; i < parameterTypes.size(); i++) {
+            JavaType paramType = parameterTypes.get(i);
+            String paramName = getParameterName(parameterNames, i);
+            String paramTypeName = buildTypeName(paramType);
+            params.add(paramTypeName + " " + paramName);
+        }
+        return String.join(", ", params);
+    }
+
+    private static String getParameterName(List<String> parameterNames, int index) {
+        if (parameterNames != null
+                && index < parameterNames.size()
+                && parameterNames.get(index) != null
+                && !parameterNames.get(index).trim().isEmpty()) {
path: src/main/java/org/openrewrite/java/dropwizard/method/util/MethodStubCreator.java
user: login: github-actions[bot]
body: ```suggestion
        if (parameterNames != null &&
                index < parameterNames.size() &&
                parameterNames.get(index) != null &&
                !parameterNames.get(index).trim().isEmpty()) {
```
created_at: 2025-04-01T05:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,254 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class AddMissingAbstractMethodsTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new AddMissingAbstractMethods())
+          .parser(
+            JavaParser.fromJavaVersion()
+              .classpath(JavaParser.runtimeClasspath())
+              .logCompilationWarningsAndErrors(false));
+    }
+
+    @Test
+    void implementsAbstractMethod() {
+        rewriteRun(
+          java(
+            """
+              abstract class AbstractParent {
+                  abstract void doSomething(String input);
+              }
+              """),
+          java(
+            """
+              class Child extends AbstractParent {
+              }
+              """,
+            """
+              class Child extends AbstractParent {
+                  @Override
+                  public void doSomething(java.lang.String input) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsInterfaceMethod() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void processData(int value);
+              }
+              """),
+          java(
+            """
+              class Implementation implements MyInterface {
+              }
+              """,
+            """
+              class Implementation implements MyInterface {
+                  @Override
+                  public void processData(int value) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsMultipleInterfaces() {
+        rewriteRun(
+          java(
+            """
+              interface FirstInterface {
+                  void methodOne();
+              }
+              """),
+          java(
+            """
+              interface SecondInterface {
+                  void methodTwo(String input);
+              }
+              """),
+          java(
+            """
+              class Implementation implements FirstInterface, SecondInterface {
+              }
+              """,
+            """
+              class Implementation implements FirstInterface, SecondInterface {
+                  @Override
+                  public void methodOne() {
+                      throw new UnsupportedOperationException();
+                  }
+
+                  @Override
+                  public void methodTwo(java.lang.String input) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void doesNotDuplicateExistingImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void process();
+              }
+              """),
+          java(
+            """
+              class Implementation implements MyInterface {
+                  @Override
+                  public void process() {
+                      System.out.println("Processing");
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void implementsAbstractMethodWithComplexParameters() {
+        rewriteRun(
+          java(
+            """
+              import java.util.List;
+              import java.util.Map;
+
+              abstract class ComplexParent {
+                  abstract <T> List<T> transform(Map<String, T> input, Class<T> type);
+              }
+              """),
+          java(
+            """
+              class Child extends ComplexParent {
+              }
+              """,
+            """
+              class Child extends ComplexParent {
+                  @Override
+                  public <T> java.util.List<T> transform(java.util.Map<java.lang.String, T> input, java.lang.Class<T> type) {
+                      throw new UnsupportedOperationException();
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void shouldNotDuplicateImplementedMethods() {
+        rewriteRun(
+          java(
+            """
+              abstract class AbstractParent {
+                  public abstract String getName();
+              }
+              """),
+          java(
+            """
+              class Child extends AbstractParent {
+                  private String name;
+
+                  public String getName() {
+                      return name;
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void shouldNotAddAnyDefaultOrObjectMethods() {
+        rewriteRun(
+          java(
+            """
+              import java.security.Principal;
+              import java.util.Set;
+
+              public class User implements Principal {
+                  private final String name;
+
+                  private final Set<String> roles;
+
+                  public User(String name) {
+                      this.name = name;
+                      this.roles = null;
+                  }
+
+                  public User(String name, Set<String> roles) {
+                      this.name = name;
+                      this.roles = roles;
+                  }
+
+                  public String getName() {
+                      return name;
+                  }
+
+                  public int getId() {
+                      return (int) (Math.random() * 100);
+                  }
+
+                  public Set<String> getRoles() {
+                      return roles;
+                  }
+              }
+              """));
+    }
+
+    @DocumentExample
+    @Test
+    void shouldTransformCommandClass() {
+        rewriteRun(
+          spec ->
+            spec.recipes(new AddMissingAbstractMethods())
+              .parser(
+                JavaParser.fromJavaVersion()
+                  .classpath("")
+                  .logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+              }
+              """,
+            """
+                import org.springframework.boot.CommandLineRunner;
+
+                public class RenderCommand implements CommandLineRunner {
+                    @Override
+                    public void run(java.lang.String[] args) throws java.lang.Exception {
+                        throw new UnsupportedOperationException();
+                    }
+                }
path: src/test/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethodsTest.java
user: login: github-actions[bot]
body: ```suggestion
              import org.springframework.boot.CommandLineRunner;

              public class RenderCommand implements CommandLineRunner {
                  @Override
                  public void run(java.lang.String[] args) throws java.lang.Exception {
                      throw new UnsupportedOperationException();
                  }
              }
```
created_at: 2025-04-01T05:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,294 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
import org.junit.jupiter.api.Test;
import org.openrewrite.DocumentExample;
```
created_at: 2025-04-01T05:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,294 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
    @Test
```
created_at: 2025-04-01T05:10:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,294 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """));
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+                  class TestClass implements MyInterface {
+                      @Override
+                      public void correctMethod() {}
+
+
+                      public void wrongMethod() {
+                      }
+
+                      @Override
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              class TestClass implements MyInterface {
                  @Override
                  public void correctMethod() {}
                  public void wrongMethod() {

                  @Override
                  public void anotherMethod() {}
              }
```
created_at: 2025-04-01T05:10:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,296 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """));
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """));
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """));
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+                  class TestClass implements MyInterface {
+                      @Override
+                      public void correctMethod() {}
+
+
+                      public void wrongMethod() {
+                      }
+
+                      @Override
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              class TestClass implements MyInterface {
                  @Override
                  public void correctMethod() {}
                  public void wrongMethod() {

                  @Override
                  public void anotherMethod() {}
              }
```
created_at: 2025-04-01T19:38:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: timtebeek
body: Did you know we already have a [RemoveImplements recipe](https://github.com/openrewrite/rewrite/blob/de210f8d4ec60ec4d01ebbac519e0bfa4f4a64b5/rewrite-java/src/main/java/org/openrewrite/java/RemoveImplements.java#L33) as well? Seems like that might be a good option here.
created_at: 2025-04-07T20:17:45Z
author_association: MEMBER
diff_hunk: @@ -135,6 +146,13 @@ recipeList:
   - org.openrewrite.java.dropwizard.annotation.AddClassAnnotationIfSuperTypeExists:
       annotationToAdd: "org.springframework.transaction.annotation.Transactional"
       targetSupertypeName: "io.dropwizard.hibernate.AbstractDAO"
+  - org.openrewrite.java.dropwizard.method.ChangeSuperType:
+      targetClass: "io.dropwizard.hibernate.AbstractDAO"
+      newSuperclass: "ee.taltech.example.AbstractJpaDAO" # Custom class; from helper library
path: src/main/resources/META-INF/rewrite/dropwizard-to-spring-boot.yml
user: login: timtebeek
body: I'm not seeing any reference to this one; might that be internal to what you're developing there?
created_at: 2025-04-07T20:19:47Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: timtebeek
body: This is a bit of an odd mix of classes used here: it seems to test that halfway through the migration we can clear out an override. Would it not make more sens to have a unit test for the full declarative recipe verify that a fully Dropwizard input is converted to the expected Spring Boot equivalent?
created_at: 2025-04-07T20:28:11Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,359 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: timtebeek
body: Similarly, and optionally, we already have [AddMissingMethodImplementation](https://github.com/openrewrite/rewrite-migrate-java/blob/82d3e81627241ef77a09e8bc4bb85ebe0d736277/src/main/java/org/openrewrite/java/migrate/AddMissingMethodImplementation.java#L32), which is a little more explicit in it's use, but then saves us having to maintain two recipes for similar functionality. Please let me know your thoughts!
created_at: 2025-04-07T20:58:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,68 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.tree.JavaType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByPackage extends Recipe {
+
+    @Option(displayName = "Package to match",
+            description = "Supertypes that match this package are to be removed",
+            example = "io.dropwizard")
+    String packageToMatch;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove supertypes by package";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes all supertypes from a specified package in class extends or implements clauses.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new RemoveSuperTypeByPackageVisitor(packageToMatch);
+    }
+
+    private static class RemoveSuperTypeByPackageVisitor extends RemoveSuperTypeVisitor {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackage.java
user: login: timtebeek
body: We tend to opt for anonymous classes unless the named inner class is reused elsewhere. In this case I think it's fine to inline this one, and should shorten things quite a bit.
created_at: 2025-04-07T21:00:54Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,68 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.tree.JavaType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByPackage extends Recipe {
+
+    @Option(displayName = "Package to match",
+            description = "Supertypes that match this package are to be removed",
+            example = "io.dropwizard")
+    String packageToMatch;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove supertypes by package";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes all supertypes from a specified package in class extends or implements clauses.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new RemoveSuperTypeByPackageVisitor(packageToMatch);
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackage.java
user: login: timtebeek
body: In think it'd make sense to use a Precondition here on `new UsesType<>(packageToMatch + ".*", false)`, such that we can restrict the set of compilation units that the visitor will run against cheaply through the precomputed `TypesInUse`.
created_at: 2025-04-07T21:02:24Z
author_association: MEMBER
diff_hunk: @@ -135,6 +146,13 @@ recipeList:
   - org.openrewrite.java.dropwizard.annotation.AddClassAnnotationIfSuperTypeExists:
       annotationToAdd: "org.springframework.transaction.annotation.Transactional"
       targetSupertypeName: "io.dropwizard.hibernate.AbstractDAO"
+  - org.openrewrite.java.dropwizard.method.ChangeSuperType:
+      targetClass: "io.dropwizard.hibernate.AbstractDAO"
+      newSuperclass: "ee.taltech.example.AbstractJpaDAO" # Custom class; from helper library
path: src/main/resources/META-INF/rewrite/dropwizard-to-spring-boot.yml
user: login: Fossur
body: > I also have some general questions regarding the migrations, basically how do we want to add new files like Security Configuration or helper classes - there are a couple of ways we could go about it 1) We can add files via recipes like [this](https://docs.openrewrite.org/recipes/text/createtextfile) - essentially would work, but 1) not good for multi-module projects 2) we do not know where to put those files 3) We lose the type information and these can't really be operated on. The other way is a helper library, where we could put those files, like SecurityConfiguration, a DAO helper class - would you be open to this idea? Any alternative are welcome as well

Essentially I wanted to discuss this, eg. the dropwizard-hibernate library gives a DAO superclass basically - the dropwizard-hibernate .jar itself is riddled with transitive dependencies that bring much of other dw dependencies also in. To get rid of as much of the  preexisting dropwizard dependencies themselves I basically  isolated the DAO class (with some modifications and put it to a helper library) - I was wondering if that is an approach that is valid here or no
created_at: 2025-04-08T11:06:28Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: Fossur
body: No; I will take a look
created_at: 2025-04-08T11:08:13Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,68 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Option;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.tree.JavaType;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveSuperTypeByPackage extends Recipe {
+
+    @Option(displayName = "Package to match",
+            description = "Supertypes that match this package are to be removed",
+            example = "io.dropwizard")
+    String packageToMatch;
+
+    @Override
+    public String getDisplayName() {
+        return "Remove supertypes by package";
+    }
+
+    @Override
+    public String getDescription() {
+        return "Removes all supertypes from a specified package in class extends or implements clauses.";
+    }
+
+    @Override
+    public TreeVisitor<?, ExecutionContext> getVisitor() {
+        return new RemoveSuperTypeByPackageVisitor(packageToMatch);
+    }
+
+    private static class RemoveSuperTypeByPackageVisitor extends RemoveSuperTypeVisitor {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackage.java
user: login: Fossur
body: I will fix
created_at: 2025-04-08T11:11:46Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: Fossur
body: Dropwizard in particular has a lot of classes that have the extends clause instead of implements, so I don't know if that would do it, whereas Spring Boot has more interfaces

created_at: 2025-04-08T11:22:45Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,359 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: Fossur
body: Yeah, I think I made this recipe before the other one existed, I wanted to make a recipe that can handle all kinds of type changes for different migration scenarios - For more of the overall context; I ran into an issue where entire method signatures change here. I realized that it's really a really a lot of work to start reworking existing methods into new forms, and much simpler to add scaffolding here - so I focused on the quality of life aspect here to automate is as much as I can. I didn't find a really good way of doing so though; I initially kinda hoped that I could just get the method information from the superclasses and kinda reput it into the classes that do the implementation, but that approach did not work, no convenient way to make the Method back into MethodDeclaration form - so I resorted to manually parsing parameters here. Ultimately, its your guys call here how you want to maintain this stuff.
created_at: 2025-04-08T11:36:26Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,359 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: Fossur
body: I don't think this would necessarily interfere with the other recipe, as ideally this could be turned on/off at will, but I understand the maintenance concerns - so your call here
created_at: 2025-04-08T12:27:24Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: Fossur
body: Or how would we be able to use the RemoveImplements here?
created_at: 2025-04-08T12:28:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: Fossur
body: The first class has an invalid Override method, the second checks that it is removed, I took the example test case from my example dw service repository, it can be repurposed though, what do you suggest?
created_at: 2025-04-08T14:06:41Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,359 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.Recipe;
+import org.openrewrite.TreeVisitor;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.JavaType;
+import org.openrewrite.java.tree.TypeTree;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.Objects.isNull;
+import static java.util.Objects.nonNull;
+import static org.openrewrite.java.dropwizard.method.util.MethodStubCreator.buildMethodStub;
+import static org.openrewrite.java.tree.Flag.Abstract;
+import static org.openrewrite.java.tree.Flag.Default;
+import static org.openrewrite.java.tree.TypeUtils.asFullyQualified;
+import static org.openrewrite.java.tree.TypeUtils.isAssignableTo;
+
+public class AddMissingAbstractMethods extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/AddMissingAbstractMethods.java
user: login: Fossur
body: I've removed the AddAbstractMethods from the PR here, still waiting for feedback on the other stuff
created_at: 2025-04-14T18:17:34Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesGenerics() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "org.example.OldParent", "org.example.NewParent", true, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class OldParent<T> {
+                  protected T value;
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesGenerics() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "org.example.OldParent", "org.example.NewParent", true, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class OldParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent<T> {
+                  protected T value;
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesGenerics() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "org.example.OldParent", "org.example.NewParent", true, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class OldParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesSuperCallWithOverrides() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+
+                  public OldParent(String test) {
+                  }
+
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesGenerics() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "org.example.OldParent", "org.example.NewParent", true, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class OldParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesSuperCallWithOverrides() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+
+                  public OldParent(String test) {
+                  }
+
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesGenerics() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "org.example.OldParent", "org.example.NewParent", true, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class OldParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesSuperCallWithOverrides() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+
+                  public OldParent(String test) {
+                  }
+
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+
+                  public Child() {
+                      super("test");
+                  }
+
+                  @Override
+                  protected void doSomething() {
+                      super.doSomething();
+                      System.out.println("child");
+                      super.doSomethingElse();
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+
+                  public Child() {
+                  }
+
+
+                  protected void doSomething() {
+                      System.out.println("child");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    @Disabled
+    @Deprecated
+    void removesConstructorSuperCall() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  public OldParent(String value) {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,322 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void changesGenerics() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "org.example.OldParent", "org.example.NewParent", true, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class OldParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent<T> {
+                  protected T value;
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent<String> {
+                  public void setValue(String value) {
+                      this.value = value;
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesSuperCallWithOverrides() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+
+                  public OldParent(String test) {
+                  }
+
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected void doSomething() {}
+                  protected void doSomethingElse() {}
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+
+                  public Child() {
+                      super("test");
+                  }
+
+                  @Override
+                  protected void doSomething() {
+                      super.doSomething();
+                      System.out.println("child");
+                      super.doSomethingElse();
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+
+                  public Child() {
+                  }
+
+
+                  protected void doSomething() {
+                      System.out.println("child");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    @Disabled
+    @Deprecated
+    void removesConstructorSuperCall() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  public OldParent(String value) {}
+              }
+              """),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  public NewParent(String value) {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,163 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+import org.openrewrite.test.TypeValidation;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperCallsRecipeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessarySuperCalls())
+          .typeValidationOptions(TypeValidation.builder().methodInvocations(false).build())
+          .parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(false));
+    }
+
+    @DocumentExample
+    @Test
+    void removesSuperCallsWhenNoSuperclass() {
+        rewriteRun(
+          java(
+            """
+              class A {
+                  void method() {
+                      super.method();
+                      System.out.println("hello");
+                  }
+              }
+              """,
+            """
+              class A {
+                  void method() {
+                      System.out.println("hello");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsSuperCallsWhenMethodIsOverridden() {
+        rewriteRun(
+          java(
+            """
+              class Parent {
+                  void method() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperCallsRecipeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,207 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByPackageTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByPackage("com.example"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackageTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,207 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByPackageTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByPackage("com.example"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackageTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,207 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByPackageTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByPackage("com.example"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          java(
+            """
+              package com.other.example;
+
+              public interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackageTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,207 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByPackageTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByPackage("com.example"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          java(
+            """
+              package com.other.example;
+
+              public interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToRemove {
+                  void removeMethod();
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackageTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,207 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByPackageTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByPackage("com.example"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          java(
+            """
+              package com.other.example;
+
+              public interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToRemove {
+                  void removeMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              import com.other.example.InterfaceToKeep;
+
+              class TestClass implements InterfaceToKeep, InterfaceToRemove {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              import com.other.example.InterfaceToKeep;
+
+              class TestClass implements InterfaceToKeep {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesGenericType() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass<T> {
+                  protected T getValue() { return null; }
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackageTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,207 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByPackageTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByPackage("com.example"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          java(
+            """
+              package com.other.example;
+
+              public interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToRemove {
+                  void removeMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              import com.other.example.InterfaceToKeep;
+
+              class TestClass implements InterfaceToKeep, InterfaceToRemove {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              import com.other.example.InterfaceToKeep;
+
+              class TestClass implements InterfaceToKeep {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesGenericType() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass<T> {
+                  protected T getValue() { return null; }
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass<String> {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotModifyUnrelatedClasses() {
+        rewriteRun(
+          java(
+            """
+              package com.other.example;
+
+              public class DifferentBase {
+                  protected void differentMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByPackageTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveSuperTypeByType("com.example.MyInterface")),
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveSuperTypeByType("com.example.MyInterface")),
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          spec -> spec.recipes(new RemoveSuperTypeByType("com.example.InterfaceToRemove")),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveSuperTypeByType("com.example.MyInterface")),
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          spec -> spec.recipes(new RemoveSuperTypeByType("com.example.InterfaceToRemove")),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToRemove {
+                  void removeMethod();
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveSuperTypeByType("com.example.MyInterface")),
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          spec -> spec.recipes(new RemoveSuperTypeByType("com.example.InterfaceToRemove")),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToRemove {
+                  void removeMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements InterfaceToKeep, InterfaceToRemove {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass implements InterfaceToKeep {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesGenericType() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass<T> {
+                  protected T getValue() { return null; }
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveSuperTypeByType("com.example.MyInterface")),
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          spec -> spec.recipes(new RemoveSuperTypeByType("com.example.InterfaceToRemove")),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToRemove {
+                  void removeMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements InterfaceToKeep, InterfaceToRemove {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass implements InterfaceToKeep {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesGenericType() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass<T> {
+                  protected T getValue() { return null; }
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass<String> {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotModifyUnrelatedClasses() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class DifferentBase {
+                  protected void differentMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,241 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveSuperTypeByTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(new RemoveSuperTypeByType("com.example.BaseClass"));
+    }
+
+    @Test
+    void removesExtends() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass {
+                  protected void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      baseMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesInterface() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveSuperTypeByType("com.example.MyInterface")),
+          java(
+            """
+              package com.example;
+
+              interface MyInterface {
+                  void interfaceMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements MyInterface {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass {
+                  @Override
+                  public void interfaceMethod() {}
+                  void someMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOneOfMultipleInterfaces() {
+        rewriteRun(
+          spec -> spec.recipes(new RemoveSuperTypeByType("com.example.InterfaceToRemove")),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToKeep {
+                  void keepMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              interface InterfaceToRemove {
+                  void removeMethod();
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class TestClass implements InterfaceToKeep, InterfaceToRemove {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """,
+            """
+              package com.example;
+
+              class TestClass implements InterfaceToKeep {
+                  @Override
+                  public void keepMethod() {}
+                  @Override
+                  public void removeMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesGenericType() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class BaseClass<T> {
+                  protected T getValue() { return null; }
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class ChildClass extends BaseClass<String> {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """,
+            """
+              package com.example;
+
+              class ChildClass {
+                  void someMethod() {
+                      String value = getValue();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotModifyUnrelatedClasses() {
+        rewriteRun(
+          java(
+            """
+              package com.example;
+
+              class DifferentBase {
+                  protected void differentMethod() {}
+              }
+              """),
+          java(
+            """
+              package com.example;
+
+              class UnrelatedClass extends DifferentBase {
+                  void someMethod() {
+                      differentMethod();
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    @Disabled
+    void handlesImportRemoval() {
+        rewriteRun(
+          java(
+            """
+              package com.example.base;
+
+              public class BaseClass {
+                  protected void importedMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveSuperTypeByTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+
+                  public void wrongMethod() {
+                  }
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesAnonymousClass() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnnecessaryOverride(true)),
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+
+                  public void wrongMethod() {
+                  }
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesAnonymousClass() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnnecessaryOverride(true)),
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass {
+                  void test() {
+                      BaseClass anon = new BaseClass() {
+                          @Override
+                          void wrongMethod() {}
+                      };
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesSuperInterface() {
+        rewriteRun(
+          java(
+            """
+              interface SuperInterface {
+                  void superMethod();
+              }
+
+              interface SubInterface extends SuperInterface {
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,314 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void keepsValidOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void baseMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesMultipleAnnotations() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void someMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  @Override
+                  void wrongMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+                  @Deprecated
+                  void wrongMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesInterfaceImplementation() {
+        rewriteRun(
+          java(
+            """
+              interface MyInterface {
+                  void correctMethod();
+                  void anotherMethod();
+              }
+              """),
+          java(
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+                  @Override
+                  public void wrongMethod() {}
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """,
+            """
+              class TestClass implements MyInterface {
+                  @Override
+                  public void correctMethod() {}
+
+
+                  public void wrongMethod() {
+                  }
+
+                  @Override
+                  public void anotherMethod() {}
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesAnonymousClass() {
+        rewriteRun(
+          spec -> spec.recipe(new RemoveUnnecessaryOverride(true)),
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
+          java(
+            """
+              class TestClass {
+                  void test() {
+                      BaseClass anon = new BaseClass() {
+                          @Override
+                          void wrongMethod() {}
+                      };
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void handlesSuperInterface() {
+        rewriteRun(
+          java(
+            """
+              interface SuperInterface {
+                  void superMethod();
+              }
+
+              interface SubInterface extends SuperInterface {
+              }
+              """),
+          java(
+            """
+              class TestClass implements SubInterface {
+                  @Override
+                  public void superMethod() {}
+
+                  @Override
+                  public void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass implements SubInterface {
+                  @Override
+                  public void superMethod() {}
+
+
+                  public void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOverrideFromNonexistentSuper() {
+        rewriteRun(
+          spec -> spec.parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              class TestClass {
+                  @Override
+                  public void execute() {
+
+                  }
+              }
+              """,
+            """
+              class TestClass {
+
+                  public void execute() {
+
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void removesOverrideFromChangedSuper() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+                new RemoveSuperTypeByType("ee.test.BaseClass"),
+                new RemoveUnnecessaryOverride(false))
+              .parser(JavaParser.fromJavaVersion().logCompilationWarningsAndErrors(false)),
+          java(
+            """
+              package ee.test;
+
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """),
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
              """
          ),
```
created_at: 2025-04-18T11:22:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,331 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
    @Test
    void handlesImportsCorrectly() {
        rewriteRun(
          spec ->
            spec.recipes(
              new ChangeSuperType(
                "java.util.Vector", "java.util.ArrayList", false, false, false)),
          java(
            """
              package org.example;

              public class Child extends java.util.Vector<String> {
                  public void someMethod() {
                      this.add("test");
                  }
              }
              """,
            """
              package org.example;

              import java.util.ArrayList;

              public class Child extends ArrayList {
                  public void someMethod() {
                      this.add("test");
                  }
              }
              """
          )
        );
    }

    @Test
```
created_at: 2025-04-28T18:04:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,331 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
    @Test
```
created_at: 2025-04-28T18:04:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,321 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
```
created_at: 2025-04-28T18:04:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,321 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @Test
    void removesUnnecessaryOverride() {
        rewriteRun(
          java(
            """
              class BaseClass {
                  void baseMethod() {}
              }
              """
          ),
          java(
            """
              class TestClass extends BaseClass {
                  @Override
                  void nonExistentMethod() {}
              }
              """,
            """
              class TestClass extends BaseClass {

                  void nonExistentMethod() {
                  }
              }
              """
          )
        );
    }

    @Test
```
created_at: 2025-04-28T18:04:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,331 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
    @Test
    void handlesImportsCorrectly() {
        rewriteRun(
          spec ->
            spec.recipes(
              new ChangeSuperType(
                "java.util.Vector", "java.util.ArrayList", false, false, false)),
          java(
            """
              package org.example;

              public class Child extends java.util.Vector<String> {
                  public void someMethod() {
                      this.add("test");
                  }
              }
              """,
            """
              package org.example;

              import java.util.ArrayList;

              public class Child extends ArrayList {
                  public void someMethod() {
                      this.add("test");
                  }
              }
              """
          )
        );
    }

    @Test
```
created_at: 2025-04-28T18:23:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,331 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
    @Test
```
created_at: 2025-04-28T18:23:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,321 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
```
created_at: 2025-04-28T18:23:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,321 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @Test
    void removesUnnecessaryOverride() {
        rewriteRun(
          java(
            """
              class BaseClass {
                  void baseMethod() {}
              }
              """
          ),
          java(
            """
              class TestClass extends BaseClass {
                  @Override
                  void nonExistentMethod() {}
              }
              """,
            """
              class TestClass extends BaseClass {

                  void nonExistentMethod() {
                  }
              }
              """
          )
        );
    }

    @Test
```
created_at: 2025-04-28T18:23:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,331 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
    @Test
    void handlesImportsCorrectly() {
        rewriteRun(
          spec ->
            spec.recipes(
              new ChangeSuperType(
                "java.util.Vector", "java.util.ArrayList", false, false, false)),
          java(
            """
              package org.example;

              public class Child extends java.util.Vector<String> {
                  public void someMethod() {
                      this.add("test");
                  }
              }
              """,
            """
              package org.example;

              import java.util.ArrayList;

              public class Child extends ArrayList {
                  public void someMethod() {
                      this.add("test");
                  }
              }
              """
          )
        );
    }

    @Test
```
created_at: 2025-04-28T18:49:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,331 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class ChangeSuperTypeTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipe(
+            new ChangeSuperType(
+              "org.example.OldParent", "org.example.NewParent", false, false, true))
+          .parser(JavaParser.fromJavaVersion().classpath("metrics-healthchecks", "spring-boot-actuator"));
+    }
+
+    @Test
+    void changesSuperclass() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class OldParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class NewParent {
+                  protected String oldField;
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends OldParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              public class Child extends org.example.NewParent {
+                  public void someMethod() {
+                      this.oldField = "test";
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
+    void doesNotChangeWhenNotMatching() {
+        rewriteRun(
+          java(
+            """
+              package org.example;
+
+              public class DifferentParent {
+              }
+              """
+          ),
+          java(
+            """
+              package org.example;
+
+              public class Child extends DifferentParent {
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void handlesImportsCorrectly() {
+        rewriteRun(
+          spec ->
+            spec.recipes(
+              new ChangeSuperType(
+                "java.util.Vector", "java.util.ArrayList", false, false, false)),
+          java(
+            """
+              package org.example;
+
+              public class Child extends java.util.Vector<String> {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """,
+            """
+              package org.example;
+
+              import java.util.ArrayList;
+
+              public class Child extends ArrayList {
+                  public void someMethod() {
+                      this.add("test");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/ChangeSuperTypeTest.java
user: login: github-actions[bot]
body: ```suggestion
    @Test
```
created_at: 2025-04-28T18:49:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,321 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @DocumentExample
```
created_at: 2025-04-28T18:49:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,321 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+
+class RemoveUnnecessaryOverrideTest implements RewriteTest {
+
+    @Override
+    public void defaults(RecipeSpec spec) {
+        spec.recipes(new RemoveUnnecessaryOverride(false));
+    }
+
+    @Test
+    void removesUnnecessaryOverride() {
+        rewriteRun(
+          java(
+            """
+              class BaseClass {
+                  void baseMethod() {}
+              }
+              """
+          ),
+          java(
+            """
+              class TestClass extends BaseClass {
+                  @Override
+                  void nonExistentMethod() {}
+              }
+              """,
+            """
+              class TestClass extends BaseClass {
+
+                  void nonExistentMethod() {
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @DocumentExample
+    @Test
+    void configuresRenderCommandArguments() {
+        rewriteRun(
+          spec ->
+            spec.parser(JavaParser.fromJavaVersion().classpath("argparse4j", "spring-boot")),
+          java(
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+                  @Override
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                               .action(Arguments.storeTrue())
+                               .dest("include-default")
+                               .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """,
+            """
+              import net.sourceforge.argparse4j.impl.Arguments;
+              import net.sourceforge.argparse4j.inf.Subparser;
+              import org.springframework.boot.CommandLineRunner;
+
+              public class RenderCommand implements CommandLineRunner {
+
+                  public void configure(Subparser subparser) {
+                      subparser.addArgument("-i", "--include-default")
+                              .action(Arguments.storeTrue())
+                              .dest("include-default")
+                              .help("Also render the template with the default name");
+                      subparser.addArgument("names").nargs("*");
+                  }
+              }
+              """
+          )
+        );
+    }
+
+    @Test
path: src/test/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverrideTest.java
user: login: github-actions[bot]
body: ```suggestion
    @Test
    void removesUnnecessaryOverride() {
        rewriteRun(
          java(
            """
              class BaseClass {
                  void baseMethod() {}
              }
              """
          ),
          java(
            """
              class TestClass extends BaseClass {
                  @Override
                  void nonExistentMethod() {}
              }
              """,
            """
              class TestClass extends BaseClass {

                  void nonExistentMethod() {
                  }
              }
              """
          )
        );
    }

    @Test
```
created_at: 2025-04-28T18:49:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+/*
+ * Copyright 2025 the original author or authors.
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * <p>
+ * https://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openrewrite.java.dropwizard.method;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.*;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.service.AnnotationService;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.TypeUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Value
+@EqualsAndHashCode(callSuper = false)
+public class RemoveUnnecessaryOverride extends Recipe {
path: src/main/java/org/openrewrite/java/dropwizard/method/RemoveUnnecessaryOverride.java
user: login: timtebeek
body: Had a closer look just now ; turns out we didn't yet need the option to remove implements, so I've stripped that down and suggest we first look at the existing recipe if the need arises in subsequent work here, thanks!
created_at: 2025-04-28T19:06:09Z
author_association: MEMBER
diff_hunk: @@ -135,6 +146,13 @@ recipeList:
   - org.openrewrite.java.dropwizard.annotation.AddClassAnnotationIfSuperTypeExists:
       annotationToAdd: "org.springframework.transaction.annotation.Transactional"
       targetSupertypeName: "io.dropwizard.hibernate.AbstractDAO"
+  - org.openrewrite.java.dropwizard.method.ChangeSuperType:
+      targetClass: "io.dropwizard.hibernate.AbstractDAO"
+      newSuperclass: "ee.taltech.example.AbstractJpaDAO" # Custom class; from helper library
path: src/main/resources/META-INF/rewrite/dropwizard-to-spring-boot.yml
user: login: timtebeek
body: Understandable that you'd want to trim that down quite a bit, especially in house. For the recipes we offer here I think it might be best not to make any surprising code changes for folks, so I've commented out this step for now so we can get the rest of the changes in.
created_at: 2025-04-28T19:34:00Z
author_association: MEMBER

PR comments:
user: login: Fossur
created_at: 2025-04-07T16:32:39Z
body: 👋 Any feedback on this?
author_association: COLLABORATOR
user: login: timtebeek
created_at: 2025-04-07T16:35:36Z
body: I'll try to get this in with a first 0.0.1 release tomorrow, but haven't found the time to go over it yet. 🙏🏻 
author_association: MEMBER
user: login: timtebeek
created_at: 2025-04-07T21:17:28Z
body: Did a first pass through, but more review / polish is needed before we can confidently merge this in. From my point of view some opportunities to reuse existing recipes were missed, which then leads to (not exact) duplication. As long term maintainer of these recipes I think it'd be good to reduce that duplication where we can, as we'd have to otherwise keep up functional copies whenever we make internal changes. Let me know if you'd be open to making those changes still!
author_association: MEMBER
user: login: Fossur
created_at: 2025-04-08T11:03:45Z
body: > Did a first pass through, but more review / polish is needed before we can confidently merge this in. From my point of view some opportunities to reuse existing recipes were missed, which then leads to (not exact) duplication. As long term maintainer of these recipes I think it'd be good to reduce that duplication where we can, as we'd have to otherwise keep up functional copies whenever we make internal changes. Let me know if you'd be open to making those changes still!

Yup, I am fine with streamlining things and reusing already existing stuff - I will get started on making the fixes

author_association: COLLABORATOR
user: login: timtebeek
created_at: 2025-04-28T19:36:12Z
body: Also: let me know when you'd consider the recipes here fit for a v0.0.1, and then we'll push that out and add it to the docs. 🙏🏻 
author_association: MEMBER