2024-10-18
"We are students, and students should act like students." --- JoJo's Bizarre Adventure · YourHouseExploded
Java String
Compared to Java 11, Java 21 introduces many new features and methods for String
operations, making string manipulation more convenient and intuitive. Below are the key new features and methods related to String
introduced in Java 21:
1. stripIndent()
(Java 13)
- Purpose: Removes common leading spaces from each line in a multi-line string, commonly used with text blocks.
- Example:
Output:
String text = """
Hello,
World!
""";
System.out.println(text.stripIndent());Hello,
World!
2. translateEscapes()
(Java 15)
- Purpose: Converts escape sequences in a string to their actual characters, such as converting
\\n
to a newline. - Example:
Output:
String str = "Hello\\nWorld";
System.out.println(str.translateEscapes());Hello
World
3. formatted()
(Java 15)
- Purpose: Formats a string, similar to
String.format()
, but more concise. - Example:
Output:
String name = "John";
String greeting = "Hello, %s".formatted(name);
System.out.println(greeting);Hello, John
4. indent(int n)
(Java 12)
- Purpose: Adds or removes a specified number of spaces of indentation to each line. Positive values add indentation, negative values remove indentation.
- Example:
Output:
String text = "Hello\nWorld";
System.out.println(text.indent(4));Hello
World
5. repeat(int count)
(Java 11)
- Purpose: Repeats the current string a specified number of times.
- Example:
Output:
String str = "Hello ";
System.out.println(str.repeat(3));Hello Hello Hello
6. isBlank()
(Java 11)
- Purpose: Checks if a string is empty or contains only whitespace.
- Example:
String str = " ";
System.out.println(str.isBlank()); // true
7. lines()
(Java 11)
- Purpose: Splits the string into a stream (Stream) of lines.
- Example:
Output:
String text = "Hello\nWorld\nJava";
text.lines().forEach(System.out::println);Hello
World
Java
8. strip()
, stripLeading()
, stripTrailing()
(Java 11)
- Purpose: Removes leading and trailing whitespace from strings.
strip()
uses the Unicode standard, making it more robust thantrim()
. - Example:
String str = " Hello World ";
System.out.println(str.strip()); // Remove both leading and trailing spaces
System.out.println(str.stripLeading()); // Remove leading spaces
System.out.println(str.stripTrailing()); // Remove trailing spaces
9. Text Blocks (Java 13, Java 15)
- Purpose: Defines multi-line strings using
"""
. Text blocks offer a more concise and readable way to write multi-line strings. - Example:
Output:
String textBlock = """
{
"name": "John",
"age": 30
}
""";
System.out.println(textBlock);{
"name": "John",
"age": 30
}
10. String.stripIndent()
and String.transform()
(Java 12)
- Purpose:
stripIndent()
: Removes common indentation from multi-line text.transform(Function<String, R>)
: Used for chaining operations on strings.
- Example:
String result = " Hello ".transform(String::strip);
System.out.println(result); // Output: "Hello"
11. StringTemplate
(Java 21)
- Purpose: Introduces string templates that can be defined using
STR.
with placeholders, providing an elegant way to perform string interpolation. - Example:
Output:
String name = "Alice";
int age = 25;
String result = STR."Hello, \{name}. You are \{age} years old.".toString();
System.out.println(result);Hello, Alice. You are 25 years old.
Summary
Compared to Java 11, Java 21 has mainly added tools for simplifying string manipulation and text processing, such as:
- Text blocks make writing multi-line strings simpler.
- New methods like
stripIndent()
,translateEscapes()
, andformatted()
provide more flexible string manipulation capabilities. - Java 21's
StringTemplate
offers a modern way to perform string interpolation, making the code more concise and readable.
These improvements greatly enhance Java's capabilities in handling strings, improving developer productivity.
Gradle maven-publish
Plugin
To configure the maven-publish
plugin in a build.gradle.kts
file using Kotlin DSL, follow these steps:
- Add the
maven-publish
plugin in theplugins
block. - Configure the
publishing
block to set up the content and the target for publishing.
Here’s a complete example:
plugins {
kotlin("jvm") version "1.9.0" // Example: Add Kotlin plugin
`maven-publish` // Add maven-publish plugin
}
group = "com.example"
version = "1.0.0"
java {
withJavadocJar()
withSourcesJar()
}
publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"]) // Publish Java component
// Optional: Set POM information
pom {
name.set("My Library")
description.set("A description of my library")
url.set("https://github.com/example/my-library")
licenses {
license {
name.set("The Apache License, Version 2.0")
url.set("http://www.apache.org/licenses/LICENSE-2.0.txt")
}
}
developers {
developer {
id.set("example")
name.set("Developer Name")
email.set("developer@example.com")
}
}
scm {
connection.set("scm:git:git://github.com/example/my-library.git")
developerConnection.set("scm:git:ssh://github.com:example/my-library.git")
url.set("https://github.com/example/my-library")
}
}
}
}
repositories {
maven {
val releasesRepoUrl = uri("https://your.repository.url/releases")
val snapshotsRepoUrl = uri("https://your.repository.url/snapshots")
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
credentials {
username = project.findProperty("repo.user") as String? ?: "defaultUser"
password = project.findProperty("repo.password") as String? ?: "defaultPassword"
}
}
}
}
Explanation:
-
Plugin Configuration:
plugins {
`maven-publish`
}Use the
maven-publish
plugin to publish to a Maven repository. -
Published Component:
from(components["java"])
Use
from(components["java"])
to publish the Java component. For Kotlin projects, you can publish thekotlin
component if needed. -
Generate Sources and Javadoc JAR:
java {
withJavadocJar()
withSourcesJar()
}This generates JAR files containing Javadoc and source code, which helps developers use the library.
-
POM Information: You can configure project metadata in the
pom
block, such as descriptions, developer information, and licenses, which will be included in the generatedPOM.xml
file. -
Target Repository:
repositories {
maven {
val releasesRepoUrl = uri("https://your.repository.url/releases")
val snapshotsRepoUrl = uri("https://your.repository.url/snapshots")
url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl
credentials {
username = project.findProperty("repo.user") as String? ?: "defaultUser"
password = project.findProperty("repo.password") as String? ?: "defaultPassword"
}
}
}Configure the target repository in
repositories
and decide whether to publish to thereleases
orsnapshots
repository based on the version.
You can then run the following command to publish your library:
./gradlew publish
This is how you configure the maven-publish
plugin in Kotlin DSL, and I hope it helps!
Differences between add()
and addProvider()
in Gradle Dependencies
Let me explain in detail the difference between the add()
and addProvider()
methods, and how they are used in your code.
Background
In Gradle, DependencyHandler
provides a series of methods for adding dependencies to a project's configuration. Common methods include:
add(String configurationName, Object dependencyNotation)
addProvider(String configurationName, Provider<?> dependencyProvider)
Both methods are used to add dependencies to a specified configuration, but they differ in how they handle and add dependencies in terms of timing.
add()
Method
The add()
method is used to add dependencies to a specified configuration immediately. Its parameters are:
configurationName
: The name of the configuration (e.g.,"implementation"
,"testCompile"
).dependencyNotation
: The dependency notation, which can be a string (e.g.,"org.apache.commons:commons-lang3:3.12.0"
), a project object, a file collection, etc.
When you call add()
, Gradle will immediately resolve and add the dependency.
Example:
project.getDependencies().add("implementation", "org.apache.commons:commons-lang3:3.12.0");
addProvider()
Method
The addProvider()
method is used to defer adding dependencies. Its parameters are:
configurationName
: The name of the configuration.dependencyProvider
: AProvider
object that provides the dependency notation.
Using addProvider()
means that the resolution and addition of the dependency are deferred until Gradle actually needs the information. This is useful when the dependency information is not determined at configuration time or needs to be computed later.
Example:
Provider<String> dependencyNotationProvider = someExtension.getVersion().map(version -> "org.example:library:" + version);
project.getDependencies().addProvider("implementation", dependencyNotationProvider);
Application in Code
Original Code Analysis
project.getDependencies().addProvider(
jooqGeneratorRuntime.getName(),
jooqExtension.getEdition()
.map(e -> e.getGroupId() + ":jooq-codegen")
.flatMap(ga -> jooqExtension.getVersion().map(v -> ga + ":" + v))
);
In this code:
- The dependency's construction is deferred: Both
jooqExtension.getEdition()
andjooqExtension.getVersion()
returnProvider
objects, meaning these values might not be determined during configuration and will only be resolved at some point in the future (e.g., during task execution). - Uses the
addProvider()
method: Since the dependency representation is aProvider<String>
, theaddProvider()
method is used to ensure the dependency is resolved and added when needed.
Adding the Current Project as a Dependency
project.getDependencies().add(jooqGeneratorRuntime.getName(), project);
In this case:
- Uses
add()
method directly: Since the dependency to be added is the currentproject
object, which is already known during configuration, there is no need to defer its addition. - Dependency is added immediately: Since the dependency is already determined, it can be added directly.
Why Differentiate Between add()
and addProvider()
-
Availability of Dependencies:
- If all information about a dependency is known during configuration (such as a fixed string or object), you can use the
add()
method. - If the dependency information will only be determined later (e.g., depending on the output of other tasks or user configuration), the
addProvider()
method should be used.
- If all information about a dependency is known during configuration (such as a fixed string or object), you can use the
-
Benefits of Deferred Configuration:
- Using
Provider
can avoid unnecessary computation during the configuration phase, improving build performance. - It ensures the dependency information is the latest, especially when version or other properties may change.
- Using
Summary
add()
method: Used to add a known dependency immediately.addProvider()
method: Used to defer adding a dependency, where the dependency information is provided by aProvider
, suitable for cases where dependency information is not determined during the configuration phase.
In your code, when adding the current project as a dependency, the dependency (project
object) is known, so the add()
method is used. In contrast, when adding the jOOQ code generator dependency, the dependency information needs to be fetched from jooqExtension
, and this information may not be available during configuration, hence the addProvider()
method is used to defer resolution and addition.
I hope this explanation helps you understand the difference between add()
and addProvider()
and their specific application in your code.