Can I have macros in Java source files

2019-01-07 05:44发布

问题:

In my program I'm reading integers form console many times. Every time, I need to type this line.

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

I'm used to C/C++ and I'm wondering if I can define something like

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

and then, in every place in my java program I can read form console as

int a = new READINT;

But I read form books Java does not support macros.

Someone please explain me why is it so, and can I do this in any other way.

回答1:

You can but you shouldn't.

The shouldn't part:

You shouldn't because using the pre-processor in that way is considered bad practice to start with, and there are better and more Java-idiomatic ways to solve this use case.


The can part: (*)

Java itself doesn't support macros. On the other hand, you could pipe the source code through the C pre processor (CPP for short) just like the C/C++ compile chain does.

Here's a demo:

src/Test.java:

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

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

cpp command:

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

Result:

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

Compile:

$ javac preprocessed/Test.java


A better workaround:

You can write your own utility class with a static method instead:

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

And when you want to use it, you can statically import the readInt method:

import static StdinUtil.readInt; 

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

(or do static import StdinUtil.STDIN; and use STDIN.nextInt().)


And finally, an anecdote

I myself used the CPP preprocessing approach on Java code once! I was creating a programming assignment for a course. I wanted to be able to easily extract a code skeleton out of the reference solution. So I just used a few #ifdefs to filter out the "secret" parts of the solution. That way I could maintain the reference solution, and easily regenerate the code skeleton.


This post has been rewritten as an article here.


(*) Since I hate answering questions with "you shouldn't". Besides, some future reader may have good reasons for wanting to use the cpp in conjunction with Java sources!



回答2:

No. Java (the language) does not support macros of any sort.

However, certain constructs can be faked or wrapped. While the example is silly (why are you creating a new scanner each time!?!?!) the below shows how it can be achieved:

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

But much better:

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

Happy coding.


For comment:

Static methods can be called without needing an object to invoke them upon. However, in most cases one is already in an object. Consider:

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();
   }
}


回答3:

Java doesn't support macros simply because the designers of Java chose not to include that functionality. The longer answer is that Java doesn't have a preprocessor the way C/C++ does and cannot perform that functionality that the preprocessor normally would. The way I would implement this is simply create a wrapper class that wraps up the Scanner constructor calls. Perhaps something like

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

Or, better yet,

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 does not support macros. IIRC, the language designers felt that macros and the resultant preparser was an unnecessary and undesirable complication.

Use a function instead:

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

Elsewhere:

Scanner input=new Scanner(System.in);

...


int a=readInt(input);

Note also, that I create the scanner once and reuse it.



回答5:

If you want to use C-Style macros then someone has written a pre-processor http://www.slashdev.ca/javapp/ I have no idea how good it is though.



回答6:

There is no macro concept in Java. If you're doing that a lot, it's a bad idea to instantiate a new Scanner each time. Define a public static Scanner then reuse it each time:

public class YourClass {
  public static final Scanner SCANNER= new Scanner(System.in);
  ...
}

// Somewhere in your code
YourClass.SCANNER.nextInt();


回答7:

Use a utility class and static import.

The utility class:

package my.utils;

public class ScannerUtils {
  private ScannerUtils() {}

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

Using the utility class:

package my.examples;

import static my.utils.ScannerUtils.*;

class Example {
  void foo() {
    int i = readInt();
  }
}

As others have said, you should probably cache your scanner, but that is a separate topic.



标签: java macros