【问题标题】:Can I have macros in Java source files我可以在 Java 源文件中有宏吗
【发布时间】:2011-09-25 09:16:49
【问题描述】:

在我的程序中,我多次从控制台读取整数。每次,我都需要输入这一行。

new Scanner(System.in).nextInt(); 

我习惯了 C/C++,我想知道我是否可以定义类似的东西

#define READINT Scanner(System.in).nextInt(); 

然后,在我的 java 程序的每个地方,我都可以将表单控制台读取为

int a = new READINT;

但是我阅读了表格书籍 Java 不支持宏。

请有人解释一下为什么会这样,我可以用其他方式吗?

【问题讨论】:

  • @Thomas Jungblut 是的。只是不需要设置新的扫描仪。现在只有我看到了。但是我可以在 java 中有一种方法来替换提到的符号名称吗
  • 封装在静态方法中基本上可以满足您的要求。不管是不是宏。方法是 Java 的核心部分,不能创建符号名。
  • @AdamJMTech 是的,谢谢。但是我们可以创建一个库类来做到这一点。 (因为 c++ 编译器扩展了对象 cin 并与系统输入链接)因此,每个 java 开发人员都获得了更健壮的编码

标签: java macros


【解决方案1】:

可以但你不应该

不应该部分:

不应该因为以 is considered bad practice 的这种方式使用预处理器开始,并且有更好和更多 Java 惯用的方法来解决这个用例。


可以部分:(*)

Java 本身不支持宏。另一方面,您可以像 C/C++ 编译链一样通过C pre processor(简称 CPP)管道源代码。

这是一个演示:

src/Test.java

#define READINT (new java.util.Scanner(System.in).nextInt())

class Test {
    public static void main(String[] args) {
        int i = READINT;
    }
}

cpp 命令:

$ cpp -P src/Test.java preprocessed/Test.java

结果:

class Test {
    public static void main(String[] args) {
        int i = (new java.util.Scanner(System.in).nextInt());
    }
}

编译:

$ javac preprocessed/Test.java


更好的解决方法:

您可以使用静态方法编写自己的实用程序类:

import java.util.Scanner;
class StdinUtil {
    public final static Scanner STDIN = new Scanner(System.in);
    public static int readInt() {
        return STDIN.nextInt();
    }
}

而当你想使用它时,你可以静态导入readInt方法:

import static StdinUtil.readInt; 

class Test {
    public static void main(String[] args) {
        int i = readInt();
    }
}

(或使用static import StdinUtil.STDIN; 并使用STDIN.nextInt()。)


最后,一个轶事

我自己曾经在 Java 代码上使用过 CPP 预处理方法!我正在为一门课程创建一个编程作业。我希望能够轻松地从参考解决方案中提取代码骨架。所以我只是使用了几个#ifdefs 来过滤掉解决方案的“秘密”部分。这样我就可以维护参考解决方案,并轻松地重新生成代码骨架。


本帖已改写为文章here


(*) 因为我讨厌用“你不应该”来回答问题。此外,一些未来的读者可能有充分的理由希望将cpp 与 Java 源代码结合使用!

【讨论】:

  • 如果您真的知道没有盒子,为什么还要跳出盒子来思考? :)
  • 但是,请注意,可能会丢失样式元素,例如个人对缩进、换行等的偏好,因此请使用此方法制作最终产品,而不是您想要的程序其他人能够轻松阅读和理解
  • @Aalok,为什么会丢失? cpp 不会重新格式化代码。
【解决方案2】:

没有。 Java(语言)不支持任何类型的宏。

但是,某些构造可以被伪造或包装。虽然这个例子很愚蠢(你为什么每次都创建一个新的扫描仪!?!?!)下面展示了它是如何实现的:

int nextInt() {
   return new Scanner(System.in).nextInt(); 
}
...
int a = nextInt();
int b = nextInt();

要好得多

Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt();
int b = scanner.nextInt();

编码愉快。


评论:

静态方法 无需对象即可调用。但是,在大多数情况下,已经在一个对象中。考虑:

public class Foo {
   static int nextIntStatic() {
     return 13;
   }

   int nextInt() {
     return 42;
   }

   void realMain () {
     // okay to call, same as this.nextInt()
     // and we are "in" the object
     int ni = nextInt();
   }

   public static void main(String[] args) {
      // okay to call, is a static method
      int nis = nextIntStatic();
      Foo f = new Foo();
      f.realMain();
   }
}

【讨论】:

  • 但是在java中我们不能调用没有对象的方法。我对么。那里没有对象调用 nextint()
  • 如果您愿意,可以将方法设为静态。
【解决方案3】:

Java 不支持宏仅仅是因为 Java 的设计者选择不包含该功能。更长的答案是 Java 没有 C/C++ 那样的预处理器,并且无法执行预处理器通常会执行的功能。我实现这一点的方法是简单地创建一个包装器类来包装Scanner 构造函数调用。也许像

public static int readInt(){
  return new Scanner(System.in).nextInt();
}

或者,更好的是,

public class ScannerWrapper{
  private static Scanner instance = null;

  public static int readInt(){
   if (instance == null){
     instance = new Scanner(System.in);
   }

   return instance.nextInt();
 }

【讨论】:

    【解决方案4】:

    Java 不支持宏。 IIRC,语言设计者认为宏和由此产生的预解析器是一种不必要且不受欢迎的复杂性。

    改用函数:

    public int readInt(Scanner inp) {
        return inp.nextint();
        }
    

    其他地方:

    Scanner input=new Scanner(System.in);
    
    ...
    
    
    int a=readInt(input);
    

    另请注意,我创建了一次扫描仪并重复使用它。

    【讨论】:

      【解决方案5】:

      如果你想使用 C 风格的宏,那么有人写了一个预处理器 http://www.slashdev.ca/javapp/ 我不知道它有多好。

      【讨论】:

        【解决方案6】:

        Java 中没有宏概念。如果您经常这样做,那么每次都实例化一个新的Scanner 是个坏主意。定义一个公共静态Scanner,然后每次都重用它:

        public class YourClass {
          public static final Scanner SCANNER= new Scanner(System.in);
          ...
        }
        
        // Somewhere in your code
        YourClass.SCANNER.nextInt();
        

        【讨论】:

        • 谢谢,但 public static final Scanner SCANNER= new Scanner(System.in);我不明白这条线。你能解释一下吗
        • 这是一个常量,属于YourClass 类(不属于它的实例)。它是公开的,因此如果您导入 YourClass,您可以在任何地方使用它。
        • 那么,它是否像一个全局变量?或者,如果我必须声明一些我应该做的全局变量。请回复
        【解决方案7】:

        您可以这样做,例如,使用Java Primitive Specializations Generator

        /* define READINT //
        new Scanner(System.in).nextInt();
        // enddefine */
        
        ...
        
        int a = /* READINT */0/**/;
        

        【讨论】:

          【解决方案8】:

          宏是文本级预处理工具,如果设计不周,可能会被严重滥用。示例:

          #define MULTIPLY( a, b ) ( a * b )
          

          MULTIPLE(1+2,3+4) 的结果是 (1+2*3+4)

          另一个例子

          #define SQUARE( a ) ((a) * (a))
          
          int x = 5;
          SQUARE(x++);
          

          将使 x 增加两次。 ((x++) * (x++))

          我认为 Java 语言设计者并没有因为这些原因购买它,因为它旨在简单。

          【讨论】:

            【解决方案9】:

            使用实用程序类和静态导入。

            实用类:

            package my.utils;
            
            public class ScannerUtils {
              private ScannerUtils() {}
            
              public static int readInt() {
                return new Scanner(System.in).nextInt();
              }
            }
            

            使用实用程序类:

            package my.examples;
            
            import static my.utils.ScannerUtils.*;
            
            class Example {
              void foo() {
                int i = readInt();
              }
            }
            

            正如其他人所说,您可能应该缓存您的扫描仪,但这是一个单独的主题。

            【讨论】:

            • 为什么不在一般的 java 库安装中包含这个实用程序类以使其遍及全球。它会更健壮。我说的对吗
            • 从我如何阅读您的评论来看,您想知道为什么核心 Java 中没有这种方法作为便利 API。在方便地对常见用例进行建模时,Java 中存在许多差距。我对这是否是这样的情况没有任何看法。
            • ...但我想补充一点,设计这样一个 API 来满足整个世界的需求会很棘手。例如,是否应该每次都创建一个新的扫描仪?如果/当 System.in 被新的 InputStream 换出时会发生什么——继续使用旧的还是使用新的?
            • 那么为了高效编码,我们只能使用传统的 scan.nextInt() 方式吗?
            • @Muthu,这是一个很好的问题,而不是被困在评论线程中。你应该问一个关于构建和缓存扫描器的正确习惯用法的单独问题。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-03-05
            • 2023-03-22
            • 2021-04-25
            相关资源
            最近更新 更多