Groovy print vs println – All Output Methods Explained with 14 Examples

This guide covers Groovy println, print, printf, System.out, and formatted output with 14 practical examples. Complete guide to console output tested on Groovy 5.x.

“The first thing every programmer learns is how to print. The last thing they master is knowing what to print and where.”

Brian Kernighan & Dennis Ritchie, The C Programming Language

Last Updated: March 2026 | Tested on: Groovy 5.x, Java 17+ | Difficulty: Beginner | Reading Time: 16 minutes

The groovy println statement is usually the first thing you write when learning the language, but there is a lot more to console output than that single method. Groovy gives you print, println, printf, System.out, System.err, and sprintf – each with its own use case for formatting, debugging, and logging.

Groovy gives you several ways to write output: print, println, printf, System.out, System.err, and even sprintf for formatting without printing. Each has its own use case. Understanding when to use which one separates beginners from developers who write clean, debuggable scripts.

In this post, we will cover every output method Groovy offers, with 14 tested examples that show exactly what gets printed and why. The examples cover printing strings, numbers, collections, maps, formatted data, and redirecting output for logging and debugging. If you want to add timing and pauses between your output statements, check out our next post on Groovy sleep and timing.

The difference between print and println in Groovy is simple: println adds a newline at the end, print does not. That is it. Both methods are added by Groovy’s GDK to every script and class, so you can call them anywhere without importing anything.

According to the Groovy Development Kit documentation, these methods are provided through the DefaultGroovyMethods class which adds them to java.lang.Object. This means every object in Groovy can be printed directly.

MethodNewlineFormattingReturnsUse Case
printNoCalls toString()voidInline output, progress indicators
printlnYesCalls toString()voidStandard line-by-line output
printfNoFormat string (%s, %d, etc.)voidFormatted/aligned output
System.out.printNoCalls toString()voidWhen you need the Java PrintStream
System.errDependsCalls toString()voidError messages, separate stream
sprintfNoFormat stringStringFormat without printing

Syntax and Basic Usage

All output methods in Groovy are simple. You do not need parentheses with println since Groovy allows omitting them for single-argument method calls. However, using parentheses is perfectly fine and sometimes clearer, especially when passing expressions.

Basic Syntax for All Output Methods

// println - with and without parentheses
println "Hello, World!"
println("Hello, World!")

// print - no newline
print "Hello, "
print "World!"
println()  // just a newline

// printf - formatted output
printf "Name: %s, Age: %d%n", "Alice", 30

// System.out directly
System.out.println("Using System.out directly")

// System.err for errors
System.err.println("This goes to stderr")

Output

Hello, World!
Hello, World!
Hello, World!
Name: Alice, Age: 30
Using System.out directly
This goes to stderr

Notice that println() with no arguments just prints a blank line. This is handy for adding visual separation in your output.

14 Practical Output Examples

Let us walk through all the output methods Groovy provides, starting with the basics and building up to more advanced patterns.

Example 1: print vs println Side by Side

This is the most fundamental comparison. The print method keeps the cursor on the same line, while println moves to a new line after printing.

Example 1: print vs println

// print keeps everything on one line
print "One "
print "Two "
print "Three"
println()  // newline to finish the line

// println puts each on its own line
println "One"
println "Two"
println "Three"

// Mixing print and println
print "Loading"
print "."
print "."
print "."
println " Done!"

Output

One Two Three
One
Two
Three
Loading... Done!

Use print when you want to build a line piece by piece. This is especially useful for progress indicators or inline formatting where you control exactly where the newline goes.

Example 2: Printing Different Data Types

Both print and println call toString() on whatever you pass them. This means you can print any object directly.

Example 2: Printing Different Data Types

// Strings
println "Hello, Groovy!"

// Numbers
println 42
println 3.14159
println 100_000_000

// Booleans
println true
println false

// null
println null

// GString interpolation
def name = "Groovy"
def version = 5
println "Welcome to ${name} ${version}!"

// Expressions inside interpolation
println "2 + 3 = ${2 + 3}"
println "Uppercase: ${'groovy'.toUpperCase()}"

Output

Hello, Groovy!
42
3.14159
100000000
true
false
null
Welcome to Groovy 5!
2 + 3 = 5
Uppercase: GROOVY

Notice that println null prints the word “null” rather than throwing an exception. Groovy handles nulls gracefully in output methods.

Example 3: GString Interpolation in println

One of the biggest reasons println feels so natural in Groovy is GString interpolation. You can embed any expression inside ${...} within a double-quoted string.

Example 3: GString Interpolation

def firstName = "Alice"
def lastName = "Johnson"
def age = 28
def scores = [95, 88, 92]

// Simple variable interpolation
println "Name: ${firstName} ${lastName}"

// Short form - no braces needed for simple variables
println "Name: $firstName $lastName"

// Expressions
println "Average score: ${scores.sum() / scores.size()}"

// Method calls
println "Uppercase: ${firstName.toUpperCase()}"
println "Name length: ${firstName.length() + lastName.length()}"

// Ternary inside interpolation
println "Status: ${age >= 18 ? 'Adult' : 'Minor'}"

// Multi-line with interpolation
println """
User Profile:
  Name:  ${firstName} ${lastName}
  Age:   ${age}
  Grade: ${scores.average() >= 90 ? 'A' : 'B'}
""".stripIndent().trim()

Output

Name: Alice Johnson
Name: Alice Johnson
Average score: 91.6666666667
Uppercase: ALICE
Name length: 12
Status: Adult
User Profile:
  Name:  Alice Johnson
  Age:   28
  Grade: A

Remember: interpolation only works with double-quoted strings. Single-quoted strings are plain java.lang.String and will print the ${} literally.

Example 4: printf – C-Style Formatted Output

Groovy’s printf works just like C’s printf or Java’s System.out.printf. It uses format specifiers to control how values are displayed. Unlike println, printf does not add a newline automatically – you need %n for that.

Example 4: printf Formatting

// Basic format specifiers
printf "String: %s%n", "Hello"
printf "Integer: %d%n", 42
printf "Float: %f%n", 3.14159
printf "Float (2 decimals): %.2f%n", 3.14159
printf "Boolean: %b%n", true

// Width and alignment
printf "Right-aligned: [%20s]%n", "Groovy"
printf "Left-aligned:  [%-20s]%n", "Groovy"
printf "Zero-padded:   [%010d]%n", 42

// Multiple arguments
printf "Name: %-10s | Age: %3d | Score: %6.2f%n", "Alice", 28, 95.5
printf "Name: %-10s | Age: %3d | Score: %6.2f%n", "Bob", 35, 87.25
printf "Name: %-10s | Age: %3d | Score: %6.2f%n", "Charlie", 22, 91.0

// Hexadecimal and octal
printf "Decimal: %d, Hex: %x, Octal: %o%n", 255, 255, 255

// Scientific notation
printf "Scientific: %e%n", 123456.789
printf "Compact:    %g%n", 123456.789

Output

String: Hello
Integer: 42
Float: 3.141590
Float (2 decimals): 3.14
Boolean: true
Right-aligned: [              Groovy]
Left-aligned:  [Groovy              ]
Zero-padded:   [0000000042]
Name: Alice      | Age:  28 | Score:  95.50
Name: Bob        | Age:  35 | Score:  87.25
Name: Charlie    | Age:  22 | Score:  91.00
Decimal: 255, Hex: ff, Octal: 377
Scientific: 1.234568e+05
Compact:    123457

The printf method is ideal when you need perfectly aligned columns, padded numbers, or specific decimal precision. Use %n instead of \n for platform-independent line endings.

Example 5: System.out Direct Access

Since Groovy runs on the JVM, you have full access to System.out. This is the same java.io.PrintStream that Java uses. Most of the time, Groovy’s built-in print and println delegate to System.out anyway, but there are times when using it directly makes sense.

Example 5: System.out Methods

// Standard System.out methods - same as Java
System.out.println("Using System.out.println")
System.out.print("Using System.out.print - ")
System.out.println("same line")
System.out.printf("Formatted: %s is %d years old%n", "Alice", 30)

// System.out is a PrintStream, so you can use all PrintStream methods
System.out.append('A' as char)
System.out.append('B' as char)
System.out.append('C' as char)
System.out.println()

// Checking what System.out actually is
println "System.out class: ${System.out.getClass().name}"

// Using the write method for raw bytes
System.out.write("Raw bytes\n".getBytes())

// Printing without the Groovy wrapper
def out = System.out
out.println("Direct reference to PrintStream")

Output

Using System.out.println
Using System.out.print - same line
Formatted: Alice is 30 years old
ABC
System.out class: java.io.PrintStream
Raw bytes
Direct reference to PrintStream

When would you use System.out directly? Mainly when you need to pass the output stream as a parameter to another method, when you need raw byte-level writing, or when you need to flush the stream explicitly.

Example 6: Writing to System.err

System.err is the standard error stream. It is a completely separate stream from System.out, which means error messages can be captured or redirected independently. This is important for scripts that pipe output between commands.

Example 6: System.err for Error Output

// Basic error output
System.err.println("ERROR: Something went wrong!")

// Using System.err for warnings
System.err.println("WARNING: Deprecated method called")

// Mixing stdout and stderr
println "Processing file: data.csv"
System.err.println("WARNING: File is larger than expected")
println "Processing complete: 1000 rows"
System.err.println("ERROR: 5 rows had invalid data")

// A helper method for error output
def error(String msg) {
    System.err.println("[ERROR] ${new Date().format('HH:mm:ss')} - ${msg}")
}

def warn(String msg) {
    System.err.println("[WARN]  ${new Date().format('HH:mm:ss')} - ${msg}")
}

// Using the helpers
error("Database connection failed")
warn("Retrying in 5 seconds")
println "Normal output continues here"

Output

Processing file: data.csv
Processing complete: 1000 rows
Normal output continues here

The output above shows only stdout. The System.err lines go to stderr, which is a separate stream. In a terminal, both stdout and stderr appear together by default. But when redirecting output, you can separate them: groovy script.groovy > output.log 2> errors.log. This is why using System.err for error messages matters.

Example 7: Printing Lists and Collections

Groovy’s println produces clean, readable output for collections out of the box. No need for Arrays.toString() like in Java.

Example 7: Printing Lists and Collections

// Lists print with square brackets
def fruits = ['Apple', 'Banana', 'Cherry']
println fruits

// Ranges
println(1..10)

// Sets
def uniqueNums = [1, 2, 3, 2, 1] as Set
println uniqueNums

// Nested lists
def matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
println matrix

// Pretty printing each element
println "--- Individual items ---"
fruits.each { println "  - ${it}" }

// Joining elements for custom formatting
println fruits.join(", ")
println fruits.join(" | ")

// Printing with index
fruits.eachWithIndex { item, index ->
    println "${index + 1}. ${item}"
}

// Formatted list output
def scores = [95, 88, 72, 91, 85]
println "\nScores: ${scores}"
println "Count:  ${scores.size()}"
println "Sum:    ${scores.sum()}"
println "Avg:    ${scores.average()}"
println "Max:    ${scores.max()}"
println "Min:    ${scores.min()}"

Output

[Apple, Banana, Cherry]
1..10
[1, 2, 3]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
--- Individual items ---
  - Apple
  - Banana
  - Cherry
Apple, Banana, Cherry
Apple | Banana | Cherry
1. Apple
2. Banana
3. Cherry

Scores: [95, 88, 72, 91, 85]
Count:  5
Sum:    431
Avg:    86.2
Max:    95
Min:    72

Example 8: Printing Maps

Maps get equally clean output. Groovy displays them as [key:value] pairs, which is much nicer than Java’s default map output.

Example 8: Printing Maps

// Simple map
def person = [name: 'Alice', age: 30, city: 'London']
println person

// Nested maps
def config = [
    database: [host: 'localhost', port: 5432],
    cache: [enabled: true, ttl: 300]
]
println config

// Pretty printing a map
println "\n--- User Details ---"
person.each { key, value ->
    printf "  %-8s: %s%n", key, value
}

// Printing map as a table
def users = [
    [name: 'Alice',   age: 30, role: 'Admin'],
    [name: 'Bob',     age: 25, role: 'User'],
    [name: 'Charlie', age: 35, role: 'Editor']
]

printf "%n%-10s %-5s %-8s%n", "Name", "Age", "Role"
printf "%-10s %-5s %-8s%n", "----", "---", "----"
users.each { user ->
    printf "%-10s %-5d %-8s%n", user.name, user.age, user.role
}

Output

[name:Alice, age:30, city:London]
[database:[host:localhost, port:5432], cache:[enabled:true, ttl:300]]

--- User Details ---
  name    : Alice
  age     : 30
  city    : London

Name       Age   Role
----       ---   ----
Alice      30    Admin
Bob        25    User
Charlie    35    Editor

Combining printf with maps lets you build formatted table output very easily. This is great for CLI tools and report scripts.

Example 9: sprintf – Format Without Printing

While printf formats and prints in one step, sprintf formats and returns a String without printing it. This is useful when you need the formatted string for further processing, logging, or storing.

Example 9: sprintf for String Formatting

// sprintf returns a formatted string
def formatted = sprintf("Hello, %s! You are %d years old.", "Alice", 30)
println formatted

// Currency formatting
def price = 1299.99
def currency = sprintf("\$%,.2f", price)
println "Price: ${currency}"

// Building formatted log entries
def logEntry(String level, String msg) {
    sprintf("[%-5s] %s - %s", level, new Date().format('yyyy-MM-dd HH:mm:ss'), msg)
}

def logs = []
logs << logEntry("INFO", "Application started")
logs << logEntry("DEBUG", "Loading configuration")
logs << logEntry("WARN", "Cache miss for key: user_42")
logs << logEntry("ERROR", "Connection timeout after 30s")

logs.each { println it }

// Padding and alignment
println "\n--- Product Report ---"
def products = [
    [name: 'Widget A', qty: 150, price: 9.99],
    [name: 'Gadget B', qty: 42, price: 29.95],
    [name: 'Doohickey C', qty: 1200, price: 4.50]
]

println sprintf("%-15s %8s %10s %12s", "Product", "Qty", "Price", "Total")
println "-" * 47
products.each { p ->
    def total = p.qty * p.price
    println sprintf("%-15s %8d %10.2f %12.2f", p.name, p.qty, p.price, total)
}

Output

Hello, Alice! You are 30 years old.
Price: $1,299.99
[INFO ] 2026-03-08 14:30:00 - Application started
[DEBUG] 2026-03-08 14:30:00 - Loading configuration
[WARN ] 2026-03-08 14:30:00 - Cache miss for key: user_42
[ERROR] 2026-03-08 14:30:00 - Connection timeout after 30s

--- Product Report ---
Product              Qty      Price        Total
-----------------------------------------------
Widget A             150       9.99      1498.50
Gadget B              42      29.95      1257.90
Doohickey C         1200       4.50      5400.00

Think of sprintf as the “quiet” version of printf. Same formatting power, but it hands you back a String instead of writing to the console.

Example 10: Print with Flush

Output in Groovy is buffered by default. When you use print (without newline), the text might not appear immediately on the console because the buffer has not been flushed yet. Calling System.out.flush() forces the buffer to be written.

Example 10: Flushing Output

// Without flush, print output may be buffered
print "Processing"
System.out.flush()  // Force the text to appear now

// Simulate a progress indicator
(1..5).each { step ->
    print "."
    System.out.flush()  // Each dot appears immediately
    Thread.sleep(100)   // Simulate work
}
println " Done!"

// Auto-flush PrintStream
def autoFlushOut = new PrintStream(System.out, true)  // true = autoFlush
autoFlushOut.print("This flushes automatically")
autoFlushOut.println()

// A helper for immediate print
def printNow(String text) {
    print text
    System.out.flush()
}

printNow("Step 1: Connecting... ")
Thread.sleep(100)
println "OK"
printNow("Step 2: Loading data... ")
Thread.sleep(100)
println "OK"
printNow("Step 3: Processing... ")
Thread.sleep(100)
println "OK"

Output

Processing..... Done!
This flushes automatically
Step 1: Connecting... OK
Step 2: Loading data... OK
Step 3: Processing... OK

Flushing matters most in long-running scripts where you want real-time feedback. Without flushing, your progress dots might all appear at once at the end rather than one by one.

Example 11: Redirecting Output to a File

Sometimes you want to capture println output and redirect it to a file or a string. You can do this by temporarily swapping System.out.

Example 11: Redirecting Output

// Capture println output to a StringWriter
def captureOutput(Closure code) {
    def originalOut = System.out
    def baos = new ByteArrayOutputStream()
    System.out = new PrintStream(baos)
    try {
        code()
    } finally {
        System.out = originalOut
    }
    return baos.toString().trim()
}

def captured = captureOutput {
    println "This was captured!"
    println "So was this."
    println "Line 3 of captured output."
}

println "Captured output:"
println "---"
println captured
println "---"

// Redirect to a file
def logFile = File.createTempFile("groovy-output-", ".log")
def originalOut = System.out
System.out = new PrintStream(new FileOutputStream(logFile))

println "This goes to the file"
println "So does this"
println "Log entry at ${new Date()}"

System.out = originalOut  // Restore console output

println "Back to console now"
println "File contents:"
println logFile.text

// Cleanup
logFile.delete()

Output

Captured output:
---
This was captured!
So was this.
Line 3 of captured output.
---
Back to console now
File contents:
This goes to the file
So does this
Log entry at Sun Mar 08 14:30:00 UTC 2026

This pattern is useful for unit testing (capturing output to verify it) and for creating simple logging without a framework. Always restore the original System.out in a finally block to avoid losing console output.

Example 12: print vs Logging Frameworks

For quick scripts and prototyping, println is fine. But for production applications, you should use a proper logging framework. Here is the difference and when to switch.

Example 12: println vs Logging

import java.util.logging.Logger
import java.util.logging.Level

// ---- Using println (quick and dirty) ----
println "[INFO] Application started"
println "[DEBUG] User count: 42"
println "[ERROR] Failed to connect to DB"

// ---- Using java.util.logging (built into JDK) ----
def log = Logger.getLogger("MyApp")

log.info("Application started")
log.fine("User count: 42")
log.severe("Failed to connect to DB")

// ---- Groovy's built-in @Log annotations ----
// In a class file, you can use:
// @groovy.util.logging.Log   (java.util.logging)
// @groovy.util.logging.Slf4j (SLF4J)
// @groovy.util.logging.Log4j2 (Log4j2)

// When to use println:
// - Quick scripts and one-off utilities
// - Debugging during development
// - Learning and prototyping
// - Scripts with no logging framework available

// When to use a logging framework:
// - Production applications
// - When you need log levels (DEBUG, INFO, WARN, ERROR)
// - When you need log rotation and file output
// - When different components need different log levels

println "\n--- Comparison ---"
println "println:  Simple, always visible, no configuration"
println "logging:  Configurable levels, output targets, formatting"

Output

[INFO] Application started
[DEBUG] User count: 42
[ERROR] Failed to connect to DB

--- Comparison ---
println:  Simple, always visible, no configuration
logging:  Configurable levels, output targets, formatting

Notice that log.fine() (equivalent to DEBUG) does not produce output by default because the default log level is INFO. This is the advantage of logging frameworks – you control what gets output without changing code.

Example 13: Real-World – Building Console Apps

Here is a practical example showing how to combine all the output methods to build a clean console application.

Example 13: Console App Output Patterns

// Helper methods for clean console output
def banner(String text) {
    def line = "=" * (text.length() + 4)
    println line
    println "| ${text} |"
    println line
}

def section(String title) {
    println "\n--- ${title} ---"
}

def status(String task, String result) {
    printf "  %-30s [%s]%n", task, result
}

def progressBar(int current, int total, int width = 30) {
    def percent = (current / total * 100) as int
    def filled = (current / total * width) as int
    def empty = width - filled
    def bar = "${'#' * filled}${'-' * empty}"
    print "\r  [${bar}] ${percent}%"
    System.out.flush()
    if (current == total) println()
}

// Use the helpers
banner("System Health Check")

section("Services")
status("Database connection", "OK")
status("Cache server", "OK")
status("Message queue", "WARN")
status("External API", "FAIL")

section("Processing Files")
def totalFiles = 10
(1..totalFiles).each { i ->
    progressBar(i, totalFiles)
    Thread.sleep(50)
}

section("Summary")
println "  Total checks: 4"
println "  Passed: 2, Warnings: 1, Failed: 1"
printf "  Uptime: %.1f%%%n", 75.0

Output

==========================
| System Health Check |
==========================

--- Services ---
  Database connection            [OK]
  Cache server                   [OK]
  Message queue                  [WARN]
  External API                   [FAIL]

--- Processing Files ---
  [##############################] 100%

--- Summary ---
  Total checks: 4
  Passed: 2, Warnings: 1, Failed: 1
  Uptime: 75.0%

Notice the use of \r (carriage return) in the progress bar. This moves the cursor back to the beginning of the line without advancing to a new line, so each update overwrites the previous one. Combined with flush(), this creates a smooth progress animation.

Example 14: Real-World – Debug Output Patterns

During development, you often need quick debug output. Here are some patterns that make debugging with println more effective.

Example 14: Debug Output Patterns

// Variable inspection
def name = "Alice"
def age = 30
def scores = [95, 88, 92]

// Quick debug dump
println "DEBUG: name=${name}, age=${age}, scores=${scores}"

// Labeled output for clarity
println ">>> name.class = ${name.getClass().simpleName}"
println ">>> scores.size = ${scores.size()}"
println ">>> scores.sum = ${scores.sum()}"

// Object dump helper
def dump(String label, Object obj) {
    println "=== ${label} ==="
    if (obj instanceof Map) {
        obj.each { k, v -> println "  ${k}: ${v} (${v?.getClass()?.simpleName})" }
    } else if (obj instanceof Collection) {
        obj.eachWithIndex { item, i -> println "  [${i}]: ${item}" }
    } else {
        println "  value: ${obj}"
        println "  type:  ${obj?.getClass()?.name}"
    }
    println "=== end ${label} ===\n"
}

// Using the dump helper
def user = [name: 'Bob', age: 25, active: true]
dump("user", user)
dump("scores", scores)
dump("single value", 42)

// Conditional debug output
def DEBUG = true
def debug = { String msg ->
    if (DEBUG) System.err.println("[DBG] ${msg}")
}

debug("Entering method processData()")
println "Processing data..."
debug("Data processed successfully, 100 records")
println "Done."

Output

DEBUG: name=Alice, age=30, scores=[95, 88, 92]
>>> name.class = String
>>> scores.size = 3
>>> scores.sum = 275
=== user ===
  name: Bob (String)
  age: 25 (Integer)
  active: true (Boolean)
=== end user ===

=== scores ===
  [0]: 95
  [1]: 88
  [2]: 92
=== end scores ===

=== single value ===
  value: 42
  type:  java.lang.Integer
=== end single value ===

Processing data...
Done.

Sending debug output to System.err is a smart pattern. Your normal program output goes to stdout and debug noise goes to stderr. When you run the script, both appear in the terminal. But when you pipe the output to a file or another command, only the clean program output goes through.

printf and Formatted Output

Let us go deeper into printf formatting. The format specifiers follow the Java Formatter specification and give you precise control over how values are displayed.

Common Format Specifiers:

SpecifierTypeExampleOutput
%sStringprintf "%s", "hi"hi
%dIntegerprintf "%d", 4242
%fFloatprintf "%.2f", 3.143.14
%bBooleanprintf "%b", truetrue
%xHexprintf "%x", 255ff
%oOctalprintf "%o", 810
%eScientificprintf "%e", 1234.51.234500e+03
%nNewlineprintf "hi%n"hi + newline

You can also use Groovy’s String.format() method, which works identically to sprintf:

String.format() vs sprintf

// Both do the same thing
def result1 = sprintf("Price: \$%.2f", 19.99)
def result2 = String.format("Price: \$%.2f", 19.99)

println result1
println result2
println "Same? ${result1 == result2}"

Output

Price: $19.99
Price: $19.99
Same? true

System.out and System.err

Understanding the relationship between Groovy’s print/println and System.out is important. Groovy’s output methods are essentially wrappers around System.out. When you call println "Hello" in a Groovy script, it delegates to System.out.println("Hello").

The two output streams available are:

  • System.out (stdout) – Standard output, used by print and println
  • System.err (stderr) – Standard error, must be called explicitly

In a terminal, both streams appear in the same console. But they can be redirected independently using shell redirection (> for stdout, 2> for stderr). This is why using System.err for error messages is a best practice in command-line tools.

Printing Collections and Maps

One area where Groovy really shines over Java is how it prints collections. In Java, printing an array gives you something like [Ljava.lang.String;@1a2b3c, which is useless. Groovy always calls a meaningful toString() and gives you the actual contents.

Collection Printing Comparison

// Arrays print their contents in Groovy (unlike Java!)
String[] javaArray = ["one", "two", "three"] as String[]
println "Array: ${javaArray}"

// Even arrays of arrays
int[][] grid = [[1, 2], [3, 4]] as int[][]
println "Grid: ${grid}"

// LinkedHashMap preserves insertion order
def orderedMap = [:] as LinkedHashMap
orderedMap.put("first", 1)
orderedMap.put("second", 2)
orderedMap.put("third", 3)
println "Ordered: ${orderedMap}"

// TreeMap sorts by key
def sortedMap = new TreeMap([c: 3, a: 1, b: 2])
println "Sorted: ${sortedMap}"

// Custom toString in your classes
class Product {
    String name
    double price
    String toString() { "${name} (\$${price})" }
}

def products = [new Product(name: 'Laptop', price: 999.99),
                new Product(name: 'Mouse', price: 29.99)]
println "Products: ${products}"

Output

Array: [one, two, three]
Grid: [[1, 2], [3, 4]]
Ordered: [first:1, second:2, third:3]
Sorted: [a:1, b:2, c:3]
Products: [Laptop ($999.99), Mouse ($29.99)]

This is one of those Groovy niceties that you start to take for granted. Overriding toString() in your classes gives you clean output everywhere your objects are printed.

We covered flushing in Example 10 and redirecting in Example 11. Here is what matters:

  • Flush when using print (no newline) and you need immediate output – especially for progress indicators
  • Redirect by swapping System.out with a new PrintStream – always restore it in a finally block
  • println typically auto-flushes because it writes a newline, but this is not guaranteed by the spec
  • You can create an auto-flushing PrintStream with new PrintStream(outputStream, true)

sprintf – Format Without Printing

We saw sprintf in Example 9. To recap: it uses the same format specifiers as printf but returns a String instead of printing to the console. This makes it perfect for building formatted strings that you will use later – in log files, database records, API responses, or any context where you need the formatted text as data rather than immediate console output.

A common question is: when should I stop using println and switch to a logging framework? Here is a simple rule of thumb:

  • Use println for scripts, prototypes, one-off utilities, learning, and quick debugging
  • Use a logging framework when your code will be part of a larger application, needs configurable log levels, requires log rotation, or needs to output to multiple destinations (file + console + remote server)

Groovy makes logging frameworks easy with built-in AST transformations: @groovy.util.logging.Log, @groovy.util.logging.Slf4j, @groovy.util.logging.Log4j2. Just annotate your class and a log field is automatically available. Check the Groovy logging documentation for details.

Real-World Use Cases

Let us tie everything together with two practical scenarios that combine multiple output techniques.

CSV Report Generator

CSV Report Generator with Mixed Output

def employees = [
    [name: 'Alice Johnson', dept: 'Engineering', salary: 95000],
    [name: 'Bob Smith', dept: 'Marketing', salary: 72000],
    [name: 'Charlie Brown', dept: 'Engineering', salary: 88000],
    [name: 'Diana Ross', dept: 'Sales', salary: 67000],
    [name: 'Eve Wilson', dept: 'Engineering', salary: 102000]
]

// Console report (stdout)
println "=" * 55
printf "%-18s %-14s %12s%n", "Name", "Department", "Salary"
println "=" * 55
employees.each { emp ->
    printf "%-18s %-14s %,12d%n", emp.name, emp.dept, emp.salary
}
println "-" * 55

// Summary stats
def totalSalary = employees.sum { it.salary }
def avgSalary = totalSalary / employees.size()
printf "%-18s %-14s %,12d%n", "TOTAL", "", totalSalary
printf "%-18s %-14s %,12.0f%n", "AVERAGE", "", avgSalary

// Department breakdown (stderr for diagnostic info)
System.err.println("\n[INFO] Department breakdown:")
employees.groupBy { it.dept }.each { dept, emps ->
    System.err.printf("[INFO]   %-14s: %d employees, avg salary: \$%,.0f%n",
        dept, emps.size(), emps.average { it.salary })
}

Output

=======================================================
Name               Department         Salary
=======================================================
Alice Johnson      Engineering        95,000
Bob Smith          Marketing          72,000
Charlie Brown      Engineering        88,000
Diana Ross         Sales              67,000
Eve Wilson         Engineering       102,000
-------------------------------------------------------
TOTAL                                424,000
AVERAGE                               84,800
[INFO] Department breakdown:
[INFO]   Engineering   : 3 employees, avg salary: $95,000
[INFO]   Marketing     : 1 employees, avg salary: $72,000
[INFO]   Sales         : 1 employees, avg salary: $67,000

Interactive Menu System

Interactive Menu Output Pattern

def showMenu() {
    println "\n╔══════════════════════════════╗"
    println "║     Groovy Task Manager      ║"
    println "╠══════════════════════════════╣"
    println "║  1. List all tasks           ║"
    println "║  2. Add new task             ║"
    println "║  3. Complete a task          ║"
    println "║  4. Show statistics          ║"
    println "║  5. Exit                     ║"
    println "╚══════════════════════════════╝"
    print "  Enter your choice (1-5): "
    System.out.flush()
}

def showStats(List tasks) {
    def total = tasks.size()
    def done = tasks.count { it.done }
    def pending = total - done

    println "\n  📊 Task Statistics"
    printf "  Total:     %d%n", total
    printf "  Completed: %d (%.0f%%)%n", done, total ? (done / total * 100) : 0
    printf "  Pending:   %d (%.0f%%)%n", pending, total ? (pending / total * 100) : 0

    // Simple bar chart
    if (total > 0) {
        def barWidth = 20
        def filledWidth = (done / total * barWidth) as int
        def bar = "${'█' * filledWidth}${'░' * (barWidth - filledWidth)}"
        println "  Progress:  [${bar}]"
    }
}

// Demo the menu
showMenu()
println "3"  // Simulating user input

// Demo the stats
def sampleTasks = [
    [name: "Write blog post", done: true],
    [name: "Review PR", done: true],
    [name: "Fix bug #42", done: false],
    [name: "Update docs", done: false],
    [name: "Deploy v2.0", done: false]
]
showStats(sampleTasks)

Output

╔══════════════════════════════╗
║     Groovy Task Manager      ║
╠══════════════════════════════╣
║  1. List all tasks           ║
║  2. Add new task             ║
║  3. Complete a task          ║
║  4. Show statistics          ║
║  5. Exit                     ║
╚══════════════════════════════╝
  Enter your choice (1-5): 3

  📊 Task Statistics
  Total:     5
  Completed: 2 (40%)
  Pending:   3 (60%)
  Progress:  [████████░░░░░░░░░░░░]

Best Practices

Here are the guidelines for using output methods effectively in Groovy:

  • Use println for general-purpose output – it is the default choice for most situations
  • Use print when building output on a single line – progress bars, inline status updates, prompts
  • Use printf for aligned tables, formatted numbers, and fixed-width output
  • Use sprintf when you need the formatted string as a value, not as console output
  • Use System.err for error messages and diagnostics – keeps them separate from program output
  • Flush after print when you need immediate output – especially in progress indicators
  • Prefer GString interpolation over concatenation or printf for simple messages
  • Switch to logging frameworks for production applications – Groovy’s @Log annotations make this easy
  • Override toString() in your classes for meaningful output when objects are printed

Conclusion

Groovy gives you a rich set of output methods that go well beyond the basic println. We covered 14 practical examples showing print, println, printf, sprintf, System.out, and System.err in action.

The key differences are simple: print keeps you on the same line, println adds a newline, printf gives you formatted control, and System.err sends output to the error stream. Combine these with GString interpolation and you have a solid toolkit for clean, professional console output.

Next up in the series, we will explore Groovy sleep and timing – how to pause execution, measure elapsed time, and build timeout patterns in your scripts.

Summary

  • println adds a newline, print does not
  • printf gives you C-style formatting with %s, %d, %f specifiers
  • sprintf formats and returns a String without printing
  • Use System.err for error messages to keep them separate from program output
  • Call System.out.flush() after print when you need immediate output
  • GString interpolation ("Hello ${name}") is the preferred way to build output strings

If you also work with build tools, CI/CD pipelines, or cloud CLIs, check out Command Playground to practice 105+ CLI tools directly in your browser — no install needed.

Up next: Groovy Sleep and Timing – Pause Execution

Frequently Asked Questions

What is the difference between print and println in Groovy?

The only difference is that println adds a newline character at the end of the output, while print does not. Both methods call toString() on the argument and write to System.out. Use println for line-by-line output and print when you want to build a line piece by piece, such as progress indicators.

How do I use printf in Groovy?

Groovy’s printf works like C’s printf and Java’s System.out.printf. Use format specifiers like %s for strings, %d for integers, %f for floats, and %n for newline. Example: printf ‘Name: %s, Age: %d%n’, ‘Alice’, 30. Unlike println, printf does not add a newline automatically.

What is sprintf in Groovy?

sprintf is like printf but instead of printing to the console, it returns a formatted String. This is useful when you need the formatted text as data – for logging, storing in a variable, or passing to another method. Example: def msg = sprintf('Score: %.2f%%', 95.5) creates ‘Score: 95.50%’ without printing it.

How do I print to stderr in Groovy?

Use System.err.println() or System.err.print() to write to the standard error stream. For example: System.err.println('ERROR: File not found'). This is important for CLI scripts because stderr can be redirected separately from stdout using shell redirection (2> errors.log).

Should I use println or a logging framework in Groovy?

Use println for quick scripts, prototyping, learning, and debugging. Switch to a logging framework (like SLF4J or Log4j2) for production applications that need configurable log levels, log rotation, or multiple output destinations. Groovy makes logging easy with annotations like @groovy.util.logging.Slf4j which automatically add a log field to your class.

Previous in Series: Groovy Copy File – All Methods Explained

Next in Series: Groovy Sleep and Timing – Pause Execution

Related Topics You Might Like:

This post is part of the Groovy & Grails Cookbook series on TechnoScripts.com

RahulAuthor posts

Avatar for Rahul

Rahul is a passionate IT professional who loves to sharing his knowledge with others and inspiring them to expand their technical knowledge. Rahul's current objective is to write informative and easy-to-understand articles to help people avoid day-to-day technical issues altogether. Follow Rahul's blog to stay informed on the latest trends in IT and gain insights into how to tackle complex technical issues. Whether you're a beginner or an expert in the field, Rahul's articles are sure to leave you feeling inspired and informed.

No comment

Leave a Reply

Your email address will not be published. Required fields are marked *