PsiSubstitutor (@NotNull MethodSignature methodSignature, @NotNull MethodSignature superMethodSignature) { PsiTypeParameter[] methodTypeParameters = methodSignature.getTypeParameters(); PsiTypeParameter[] superTypeParameters = superMethodSignature.getTypeParameters(); // both methods are parameterized and number of parameters mismatch if (methodTypeParameters.length != superTypeParameters.length) return null; PsiSubstitutor result = superMethodSignature.getSubstitutor(); for (int i = 0; i < methodTypeParameters.length; i++) { PsiTypeParameter methodTypeParameter = methodTypeParameters[i]; PsiElementFactory factory = JavaPsiFacade.getElementFactory(methodTypeParameter.getProject()); result = result.put(superTypeParameters[i], factory.createType(methodTypeParameter)); } final PsiSubstitutor methodSubstitutor = methodSignature.getSubstitutor(); //check bounds for (int i = 0; i < methodTypeParameters.length; i++) { PsiTypeParameter methodTypeParameter = methodTypeParameters[i]; PsiTypeParameter superTypeParameter = superTypeParameters[i]; final Set<PsiType> methodSupers = new HashSet<>(); for (PsiClassType methodSuper : methodTypeParameter.getSuperTypes()) { methodSupers.add(methodSubstitutor.substitute(methodSuper)); } final Set<PsiType> superSupers = new HashSet<>(); for (PsiClassType superSuper : superTypeParameter.getSuperTypes()) { superSupers.add(methodSubstitutor.substitute(result.substitute(superSuper))); } methodSupers.remove(PsiType.getJavaLangObject(methodTypeParameter.getManager(), methodTypeParameter.getResolveScope())); superSupers.remove(PsiType.getJavaLangObject(superTypeParameter.getManager(), superTypeParameter.getResolveScope())); if (!methodSupers.equals(superSupers)) return null; } return result; }