Ответ 1
Даже для сценария "Before Java 8" ваш фрагмент кода неверен. Но сбор всех методов не является обычным сценарием, поскольку, как правило, вам нужны методы, относящиеся к определенному контексту, например. вы можете узнать, какие методы доступны для данного контекста, который не включает все методы, даже если вы рассматриваете методы не public
. Если вам действительно нужны все методы, вы должны вспомнить, что методы private
и static
никогда не переопределяются, а методы private-private переопределяются только при объявлении в одном и том же package
. Таким образом, он не подходит для фильтрации каждой встреченной сигнатуры метода.
Что еще хуже, так это то, что методы могут быть переопределены с помощью разных модификаторов. Последнее можно решить, сохранив идею, чтобы начать с фактического класса и использовать Class.getMethods()
для получения всего метода public
, включая методы default
, и пройти иерархию суперкласса по направлению к java.lang.Object
, так что уже встречающиеся переопределения имеют наименьшее ограничение модификаторы доступа.
В качестве побочного примечания, встраивание линейных циклов поиска никогда не является хорошей идеей. Youll скоро закончится с квадратичной или худшей сложностью.
Вы можете собирать методы, используя следующий метод:
public static Set<Method> getAllMethods(Class<?> cl) {
Set<Method> methods=new LinkedHashSet<>();
Collections.addAll(methods, cl.getMethods());
Map<Object,Set<Package>> types=new HashMap<>();
final Set<Package> pkgIndependent = Collections.emptySet();
for(Method m: methods) types.put(methodKey(m), pkgIndependent);
for(Class<?> current=cl; current!=null; current=current.getSuperclass()) {
for(Method m: current.getDeclaredMethods()) {
final int mod = m.getModifiers(),
access=Modifier.PUBLIC|Modifier.PROTECTED|Modifier.PRIVATE;
if(!Modifier.isStatic(mod)) switch(mod&access) {
case Modifier.PUBLIC: continue;
default:
Set<Package> pkg=
types.computeIfAbsent(methodKey(m), key -> new HashSet<>());
if(pkg!=pkgIndependent && pkg.add(current.getPackage())) break;
else continue;
case Modifier.PROTECTED:
if(types.putIfAbsent(methodKey(m), pkgIndependent)!=null) continue;
// otherwise fall-through
case Modifier.PRIVATE:
}
methods.add(m);
}
}
return methods;
}
private static Object methodKey(Method m) {
return Arrays.asList(m.getName(),
MethodType.methodType(m.getReturnType(), m.getParameterTypes()));
}
Но, как сказано, может быть, это не подходит для того, что вы хотите сделать. Сначала вы должны задать себе следующие вопросы:
- Вы ищете методы, которые составляют API (обычно это
public
иprotected
)? - Или вы действительно хотите видеть методы, доступные для определенного контекста
class
/package
? - Включены методы
static
? - Включены ли синтетические/мостиковые методы?
- и др.
Вот пересмотренный метод, адаптированный к вашему более конкретному запросу:
public static Collection<Method> getAllMethods(Class clazz,
boolean includeAllPackageAndPrivateMethodsOfSuperclasses,
boolean includeOverridenAndHidden) {
Predicate<Method> include = m -> !m.isBridge() && !m.isSynthetic() &&
Character.isJavaIdentifierStart(m.getName().charAt(0))
&& m.getName().chars().skip(1).allMatch(Character::isJavaIdentifierPart);
Set<Method> methods = new LinkedHashSet<>();
Collections.addAll(methods, clazz.getMethods());
methods.removeIf(include.negate());
Stream.of(clazz.getDeclaredMethods()).filter(include).forEach(methods::add);
final int access=Modifier.PUBLIC|Modifier.PROTECTED|Modifier.PRIVATE;
Package p = clazz.getPackage();
if(!includeAllPackageAndPrivateMethodsOfSuperclasses) {
int pass = includeOverridenAndHidden?
Modifier.PUBLIC|Modifier.PROTECTED: Modifier.PROTECTED;
include = include.and(m -> { int mod = m.getModifiers();
return (mod&pass)!=0
|| (mod&access)==0 && m.getDeclaringClass().getPackage()==p;
});
}
if(!includeOverridenAndHidden) {
Map<Object,Set<Package>> types = new HashMap<>();
final Set<Package> pkgIndependent = Collections.emptySet();
for(Method m: methods) {
int acc=m.getModifiers()&access;
if(acc==Modifier.PRIVATE) continue;
if(acc!=0) types.put(methodKey(m), pkgIndependent);
else types.computeIfAbsent(methodKey(m),x->new HashSet<>()).add(p);
}
include = include.and(m -> { int acc = m.getModifiers()&access;
return acc!=0? acc==Modifier.PRIVATE
|| types.putIfAbsent(methodKey(m), pkgIndependent)==null:
noPkgOverride(m, types, pkgIndependent);
});
}
for(clazz=clazz.getSuperclass(); clazz!=null; clazz=clazz.getSuperclass())
Stream.of(clazz.getDeclaredMethods()).filter(include).forEach(methods::add);
return methods;
}
static boolean noPkgOverride(
Method m, Map<Object,Set<Package>> types, Set<Package> pkgIndependent) {
Set<Package> pkg = types.computeIfAbsent(methodKey(m), key -> new HashSet<>());
return pkg!=pkgIndependent && pkg.add(m.getDeclaringClass().getPackage());
}
private static Object methodKey(Method m) {
return Arrays.asList(m.getName(),
MethodType.methodType(m.getReturnType(), m.getParameterTypes()));
}