You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

IntelliJ IDEA类信息查询及插件中程序化获取类信息方法

关于IntelliJ IDEA类信息查询与插件开发的解决方案

嘿,我来帮你拆解这两个问题——先讲在IDEA里手动查找类相关信息的方法,再教你怎么在插件里通过代码获取这些数据,包括你给出的actionPerformed方法该怎么修改~

一、在IntelliJ IDEA中手动查找类相关信息

如果你只是手动查看类的信息,IDEA自带了超多便捷工具:

  • 快速定位类本身:按下Ctrl+N(Windows/Linux)或Cmd+O(Mac),输入类名就能直接跳转到类文件,还能看到类的继承/实现层级(点击类名旁的小箭头展开)。
  • 查看类的结构(方法/字段):打开类文件后,按下Alt+7(Windows/Linux)或Cmd+7(Mac)调出Structure面板,这里能看到类的所有方法、字段、内部类,还能按类型筛选,点击就能直接跳转到对应位置。
  • 查找类的所有引用:右键类名选择「Find Usages」(或按下Alt+F7),能看到整个项目里哪里用到了这个类,包括继承、调用、实例化等场景,还能过滤引用类型。
  • 获取代码统计数据:用IDEA自带的Code Metrics功能,右键类文件选择「Analyze > Calculate Code Metrics」,就能得到类的代码行数、方法数量、圈复杂度等统计数据。

二、通过IntelliJ IDEA插件编程获取类的详细信息

你要开发插件的话,核心是用IntelliJ的PSI(Program Structure Interface)——这是平台用来解析代码结构的核心API,能让你直接操作类、方法、字段等代码元素。先修正你给出的代码里的小问题:event.getClass()获取的是AnActionEvent类,不是用户选中的目标类,我们需要先拿到用户选中的类元素,再分析它的信息。

第一步:修改actionPerformed方法,获取目标类

先从事件中提取用户选中的类(支持选中类节点或打开类文件两种场景):

import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.ui.Messages;
import com.intellij.psi.*;

public class ClassInfoAction extends AnAction {
    @Override
    public void actionPerformed(AnActionEvent event) {
        Project project = event.getProject();
        if (project == null) return;

        // 尝试获取选中的PSI元素
        PsiElement selectedElement = CommonDataKeys.PSI_ELEMENT.getData(event.getDataContext());
        PsiClass targetClass = null;

        // 场景1:用户选中了类节点或类中的元素
        if (selectedElement instanceof PsiClass) {
            targetClass = (PsiClass) selectedElement;
        }
        // 场景2:用户打开了Java文件,选中的是文件或其中的非类元素
        else {
            PsiFile psiFile = CommonDataKeys.PSI_FILE.getData(event.getDataContext());
            if (psiFile instanceof JavaPsiFile) {
                PsiClass[] classes = ((JavaPsiFile) psiFile).getClasses();
                if (classes.length > 0) {
                    targetClass = classes[0]; // 取文件中的第一个类
                }
            }
        }

        if (targetClass == null) {
            Messages.showInfoMessage(project, "请选中一个Java类或打开Java文件", "提示");
            return;
        }

        // 调用分析方法获取类信息
        analyzeClassDetails(project, targetClass);
    }
}

第二步:实现类信息分析方法analyzeClassDetails

这个方法会获取你需要的所有数据:类的代码行数、方法数量与名称、每个方法的行数,还有方法引用(用于分析耦合/内聚):

private void analyzeClassDetails(Project project, PsiClass targetClass) {
    StringBuilder result = new StringBuilder();
    String className = targetClass.getQualifiedName() != null ? targetClass.getQualifiedName() : targetClass.getName();
    result.append("类信息:").append(className).append("\n\n");

    // 1. 类的代码行数(精确到类本身的范围)
    PsiFile containingFile = targetClass.getContainingFile();
    if (containingFile != null) {
        TextRange classTextRange = targetClass.getTextRange();
        String classContent = containingFile.getText().substring(classTextRange.getStartOffset(), classTextRange.getEndOffset());
        int classLineCount = classContent.split("\n").length;
        result.append("类本身的代码行数:").append(classLineCount).append("\n");
        result.append("类所在文件的总行数:").append(containingFile.getText().split("\n").length).append("\n\n");
    }

    // 2. 方法的数量、名称与每个方法的代码行数
    PsiMethod[] methods = targetClass.getMethods();
    result.append("方法总数:").append(methods.length).append("\n");
    for (PsiMethod method : methods) {
        // 跳过构造方法(如果不需要可以去掉这个判断)
        if (method.isConstructor()) continue;

        TextRange methodRange = method.getTextRange();
        String methodContent = containingFile.getText().substring(methodRange.getStartOffset(), methodRange.getEndOffset());
        int methodLineCount = methodContent.split("\n").length;
        result.append("- 方法名:").append(method.getName()).append(",行数:").append(methodLineCount).append("\n");
    }
    result.append("\n");

    // 3. 方法引用分析(耦合与内聚)
    result.append("方法调用关系分析:\n");
    for (PsiMethod currentMethod : methods) {
        // 遍历方法内的所有方法调用
        currentMethod.accept(new JavaRecursiveElementVisitor() {
            @Override
            public void visitMethodCallExpression(PsiMethodCallExpression callExpr) {
                super.visitMethodCallExpression(callExpr);
                PsiMethod calledMethod = callExpr.resolveMethod();
                if (calledMethod == null) return;

                PsiClass calledClass = calledMethod.getContainingClass();
                if (calledClass == targetClass) {
                    // 调用当前类的方法,属于内聚
                    result.append(currentMethod.getName()).append(" 调用了内部方法:").append(calledMethod.getName()).append("\n");
                } else {
                    // 调用外部类的方法,属于耦合
                    String calledClassName = calledClass.getQualifiedName() != null ? calledClass.getQualifiedName() : calledClass.getName();
                    result.append(currentMethod.getName()).append(" 调用了外部方法:").append(calledClassName).append(".").append(calledMethod.getName()).append("\n");
                }
            }
        });
    }

    // 弹出窗口显示结果
    Messages.showInfoMessage(project, result.toString(), "类信息分析结果");
}

额外注意事项

  • 依赖导入:确保你的插件项目中已经引入了com.intellij.psi相关的依赖(在plugin.xml中声明必要的权限,比如com.intellij.modules.java)。
  • 空值处理:比如getQualifiedName()可能返回null(匿名类、本地类),所以要做非空判断。
  • 代码行数精度:上面的示例是按换行符统计,如果需要排除空行、注释,可以扩展逻辑,比如用IDEA的LineNumberCalculator或者自己过滤空白行和注释行。
  • 耦合内聚扩展:你可以进一步统计每个方法的内部调用占比、外部调用次数,来量化类的内聚度和耦合度。

内容的提问来源于stack exchange,提问作者Bharthan

火山引擎 最新活动