Улучшить алгоритм разрешения переопределения / абстрактного метода
Следующие фрагменты кода определяют, как компилятор для моего пользовательского языка программирования JVM разрешает отсутствующие абстрактные методы и связывает методы с их методами супертипа. Модель наследования похожа на Java 8, вы можете иметь как абстрактные, так и конкретные (по умолчанию) методы в интерфейсах, а также абстрактные классы и конкретные методы только в неабстрактных классах. Множественное наследование разрешено только для интерфейсов:
CodeClass.java
:
public class CodeClass extends AbstractClass
{
// ...
public void checkTypes(MarkerList markers, IContext context)
{
// ...
if (this.superType != null)
{
this.superType.getTheClass().checkMethods(markers, this, this.superType);
}
for (int i = 0; i < this.interfaceCount; i++)
{
IType type = this.interfaces[i];
type.getTheClass().checkMethods(markers, this, type);
}
}
// ...
}
AbstractClass.java
:
public class AbstractClass
{
protected ClassBody body;
// ...
public boolean checkImplements(MarkerList markers, IClass iclass, IMethod candidate, ITypeContext typeContext)
{
if (candidate.getTheClass() == this)
{
return !candidate.hasModifier(Modifiers.ABSTRACT);
}
if (this.body != null && this.body.checkImplements(markers, iclass, candidate, typeContext))
{
return true;
}
if (this.superType != null)
{
if (this.superType.getTheClass().checkImplements(markers, iclass, candidate, this.superType.getConcreteType(typeContext)))
{
return true;
}
}
for (int i = 0; i < this.interfaceCount; i++)
{
IType type = this.interfaces[i];
if (type.getTheClass().checkImplements(markers, iclass, candidate, type.getConcreteType(typeContext)))
{
return true;
}
}
return false;
}
public void checkMethods(MarkerList markers, IClass iclass, ITypeContext typeContext)
{
if (this.body != null)
{
this.body.checkMethods(markers, iclass, typeContext);
}
if (this.superType != null)
{
this.superType.getTheClass().checkMethods(markers, iclass, this.superType.getConcreteType(typeContext));
}
for (int i = 0; i < this.interfaceCount; i++)
{
IType type = this.interfaces[i];
type.getTheClass().checkMethods(markers, iclass, type.getConcreteType(typeContext));
}
}
// ...
}
ClassBody.java
:
public class ClassBody
{
// ...
public boolean checkImplements(MarkerList markers, IClass iclass, IMethod candidate, ITypeContext typeContext)
{
for (int i = 0; i < this.methodCount; i++)
{
if (this.methods[i].checkOverride(markers, iclass, candidate, typeContext))
{
return true;
}
}
return false;
}
public void checkMethods(MarkerList markers, IClass iclass, ITypeContext typeContext)
{
for (int i = 0; i < this.methodCount; i++)
{
IMethod candidate = this.methods[i];
if (iclass.checkImplements(markers, iclass, candidate, typeContext))
{
continue;
}
if (candidate.hasModifier(Modifiers.ABSTRACT) && !iclass.hasModifier(Modifiers.ABSTRACT))
{
markers.add(iclass.getPosition(), "class.method.abstract", iclass.getName(), candidate.getName(), this.theClass.getName());
}
}
}
// ...
}
method.checkOverride
просто проверяет, совпадают ли подпись кандидата и получателя, и iclass
Параметр и контейнерный класс метода одного типа, кандидат добавляется в список переопределенных методов.
Этот код хорош и симпатичен, а также работает так, как я и предполагал, но я боюсь, что он взорвется для глубокой иерархии классов с большим количеством методов (например, каркас коллекции), потому что весь процесс является сильно рекурсивным операция, которая требует много-много проверок типов и многократного перебора массивов методов. Существует ли менее сложное и более производительное решение для больших деревьев классов и как работают другие компиляторы (например, javac
или же scalac
) решить эту проблему?