Posts Tagged ‘Kotlin’
Advanced Encoding in Java, Kotlin, Node.js, and Python
Encoding is essential for handling text, binary data, and secure transmission across applications. Understanding advanced encoding techniques can help prevent data corruption and ensure smooth interoperability across systems. This post explores key encoding challenges and how Java/Kotlin, Node.js, and Python tackle them.
1️⃣ Handling Special Unicode Characters (Emoji, Accents, RTL Text)
Java/Kotlin
Java uses UTF-16 internally, but for external data (JSON, databases, APIs), explicit encoding is required:
String text = "🔧 Café مرحبا";
byte[] utf8Bytes = text.getBytes(StandardCharsets.UTF_8);
String decoded = new String(utf8Bytes, StandardCharsets.UTF_8);
System.out.println(decoded); // 🔧 Café مرحبا
✅ Tip: Always specify StandardCharsets.UTF_8
to avoid platform-dependent defaults.
Node.js
const text = "🔧 Café مرحبا";
const utf8Buffer = Buffer.from(text, 'utf8');
const decoded = utf8Buffer.toString('utf8');
console.log(decoded); // 🔧 Café مرحبا
✅ Tip: Using an incorrect encoding (e.g., latin1
) may corrupt characters.
Python
text = "🔧 Café مرحبا"
utf8_bytes = text.encode("utf-8")
decoded = utf8_bytes.decode("utf-8")
print(decoded) # 🔧 Café مرحبا
✅ Tip: Python 3 handles Unicode by default, but explicit encoding is always recommended.
2️⃣ Encoding Binary Data for Transmission (Base64, Hex, Binary Files)
Java/Kotlin
byte[] data = "Hello World".getBytes(StandardCharsets.UTF_8);
String base64Encoded = Base64.getEncoder().encodeToString(data);
byte[] decoded = Base64.getDecoder().decode(base64Encoded);
System.out.println(new String(decoded, StandardCharsets.UTF_8)); // Hello World
Node.js
const data = Buffer.from("Hello World", 'utf8');
const base64Encoded = data.toString('base64');
const decoded = Buffer.from(base64Encoded, 'base64').toString('utf8');
console.log(decoded); // Hello World
Python
import base64
data = "Hello World".encode("utf-8")
base64_encoded = base64.b64encode(data).decode("utf-8")
decoded = base64.b64decode(base64_encoded).decode("utf-8")
print(decoded) # Hello World
✅ Tip: Base64 encoding increases data size (~33% overhead), which can be a concern for large files.
3️⃣ Charset Mismatches and Cross-Language Encoding Issues
A file encoded in ISO-8859-1 (Latin-1) may cause garbled text when read using UTF-8.
Java/Kotlin Solution:
byte[] bytes = Files.readAllBytes(Paths.get("file.txt"));
String text = new String(bytes, StandardCharsets.ISO_8859_1);
Node.js Solution:
const fs = require('fs');
const text = fs.readFileSync("file.txt", { encoding: "latin1" });
Python Solution:
with open("file.txt", "r", encoding="ISO-8859-1") as f:
text = f.read()
✅ Tip: Always specify encoding explicitly when working with external files.
4️⃣ URL Encoding and Decoding
Java/Kotlin
String encoded = URLEncoder.encode("Hello World!", StandardCharsets.UTF_8);
String decoded = URLDecoder.decode(encoded, StandardCharsets.UTF_8);
Node.js
const encoded = encodeURIComponent("Hello World!");
const decoded = decodeURIComponent(encoded);
Python
from urllib.parse import quote, unquote
encoded = quote("Hello World!")
decoded = unquote(encoded)
✅ Tip: Use UTF-8 for URL encoding to prevent inconsistencies across different platforms.
Conclusion: Choosing the Right Approach
- Java/Kotlin: Strong type safety, but requires careful
Charset
management. - Node.js: Web-friendly but depends heavily on
Buffer
conversions. - Python: Simple and concise, though strict type conversions must be managed.
📌 Pro Tip: Always be explicit about encoding when handling external data (APIs, files, databases) to avoid corruption.
CTO Perspective: Choosing a Tech Stack for Mainframe Rebuild
Original post
From LinkedIn: https://www.linkedin.com/posts/matthias-patzak_cto-technology-activity-7312449287647375360-ogNg?utm_source=share&utm_medium=member_desktop&rcm=ACoAAAAWqBcBNS5uEX9jPi1JPdGxlnWwMBjXwaw
Summary of the question
As CTO for a mainframe rebuild (core banking/insurance/retail app, 100 teams/1000 people with Cobol expertise), considering Java/Kotlin, TypeScript/Node.js, Go, and Python. Key decision criteria are technical maturity/stability, robust community, and innovation/adoption. The CTO finds these criteria sound and seeks a language recommendation.
TL;DR: my response
- Team, mainframe rebuild: Java/Kotlin are frontrunners due to maturity, ecosystem, and team’s Java-adjacent skills. Go has niche potential. TypeScript/Node.js and Python less ideal for core.
- Focus now: deep PoC comparing Java (Spring Boot) vs. Kotlin on our use cases. Evaluate developer productivity, readability, interoperability, performance.
- Develop comprehensive Java/Kotlin training for our 100 Cobol-experienced teams.
- Strategic adoption plan (Java, Kotlin, or hybrid) based on PoC and team input is next.
- This balances proven stability with modern practices on the JVM for our core.
My detailed opinion
As a CTO with experience in these large-scale transformations, my priority remains a solution that balances technical strength with the pragmatic realities of our team’s current expertise and long-term maintainability.
While Go offers compelling performance characteristics, the specific demands of our core business application – be it in banking, insurance, or retail – often prioritize a mature ecosystem, robust enterprise patterns, and a more gradual transition path for our significant team. Given our 100 teams deeply skilled in Cobol, the learning curve and the availability of readily transferable concepts become key considerations.
Therefore, while acknowledging Go’s strengths in certain cloud-native scenarios, I want to emphasize the strategic advantages of the Java/Kotlin ecosystem for our primary language choice, with a deliberate hesitation and deeper exploration between these two JVM-based options.
Re-emphasizing Java and Exploring Kotlin More Deeply:
-
Java’s Enduring Strength: Java’s decades of proven stability in building mission-critical enterprise systems cannot be overstated. The JVM’s resilience, the vast array of mature libraries and frameworks (especially Spring Boot), and the well-established architectural patterns provide a solid and predictable foundation. Moreover, the sheer size of the Java developer community ensures a deep pool of talent and readily available support for our teams as they transition. For a core system in a regulated industry, this level of established maturity significantly mitigates risk.
-
Kotlin’s Modern Edge and Interoperability: Kotlin presents a compelling evolution on the JVM. Its modern syntax, null safety features, and concise code can lead to increased developer productivity and reduced boilerplate – benefits I’ve witnessed firsthand in JVM-based projects. Crucially, Kotlin’s seamless interoperability with Java is a major strategic advantage. It allows us to:
- Gradually adopt Kotlin: Teams can start by integrating Kotlin into existing Java codebases, allowing for a phased learning process without a complete overhaul.
- Leverage the entire Java ecosystem: Kotlin developers can effortlessly use any Java library or framework, giving us access to the vast resources of the Java world.
- Attract modern talent: Kotlin’s growing popularity can help us attract developers who are excited about working with a modern, yet stable, language on a proven platform.
Why Hesitate Between Java and Kotlin?
The decision of whether to primarily adopt Java or Kotlin (or a strategic mix) requires careful consideration of our team’s specific needs and the long-term vision:
- Learning Curve: While Kotlin is designed to be approachable for Java developers, there is still a learning curve associated with its new syntax and features. We need to assess how quickly our large Cobol-experienced team can become proficient in Kotlin.
- Team Preference and Buy-in: Understanding our developers’ preferences and ensuring buy-in for the chosen language is crucial for successful adoption.
- Long-Term Ecosystem Evolution: While both Java and Kotlin have strong futures on the JVM, we need to consider the long-term trends and the level of investment in each language within the enterprise space.
- Specific Use Cases: Certain parts of our system might benefit more from Kotlin’s conciseness or specific features, while other more established components might initially remain in Java.
Proposed Next Steps (Revised Focus):
- Targeted Proof of Concept (PoC) – Deep Dive into Java and Kotlin: Instead of a broad PoC including Go, let’s focus our initial efforts on a detailed comparison of Java (using Spring Boot) and Kotlin on representative use cases from our core business application. This PoC should specifically evaluate:
- Developer Productivity: How quickly can teams with a Java-adjacent mindset (after initial training) develop and maintain code in both languages?
- Code Readability and Maintainability: How do the resulting codebases compare in terms of clarity and ease of understanding for a large team?
- Interoperability Scenarios: How seamlessly can Java and Kotlin code coexist and interact within the same project?
- Performance Benchmarking: While the JVM provides a solid base, are there noticeable performance differences for our specific workloads?
- Comprehensive Training and Upskilling Program: We need to develop a detailed training program that caters to our team’s Cobol background and provides clear pathways for learning both Java and Kotlin. This program should include hands-on exercises and mentorship opportunities.
- Strategic Adoption Plan: Based on the PoC results and team feedback, we’ll develop a strategic adoption plan that outlines whether we’ll primarily focus on Java, Kotlin, or a hybrid approach. This plan should consider the long-term maintainability and talent acquisition goals.
While Go remains a valuable technology for specific niches, for the core of our mainframe rebuild, our focus should now be on leveraging the mature and evolving Java/Kotlin ecosystem and strategically determining the optimal path for our large and experienced team. This approach minimizes risk while embracing modern development practices on a proven platform.
[KotlinConf2024] Hacking Sony Cameras with Kotlin
At KotlinConf2024, Rahul Ravikumar, a Google software engineer, shared his adventure reverse-engineering Sony’s Bluetooth Low Energy (BLE) protocol to build a Kotlin Multiplatform (KMP) remote camera app. Frustrated by Sony’s bloated apps—Imaging Edge Mobile (1.9 stars) and Creators’ App (3.3 stars)—Rahul crafted a lean solution for his Sony Alpha a7r Mark 5, focusing on remote control. Using Compose Multiplatform for desktop and mobile, and Sony’s C SDK via cinterop, he demonstrated how KMP enables cross-platform innovation. His live demo, clicking a photo with a single button, thrilled the audience and showcased BLE’s potential for fun and profit.
Reverse-Engineering Sony’s BLE Protocol
Rahul’s journey began with Sony’s underwhelming app ecosystem, prompting him to reverse-engineer the camera’s undocumented BLE protocol. BLE’s Generic Access Profile (GAP) handles device discovery, with the camera (peripheral) advertising its presence and the phone (central) connecting. The Generic Attribute Profile (GATT) manages commands, using 16-bit UUIDs for services like Sony’s remote control (FF01 for commands, FF02 for notifications). Unable to use Android’s HCI Snoop logs due to Sony’s Wi-Fi Direct reliance, Rahul employed a USB BLE sniffer and Wireshark to capture GATT traffic. He identified Sony’s company ID (0x02D01) and camera marker (0x03000) in advertising packets. Key operations—reset (0x0106), focus (0x0107), and capture (0x0109)—form a state machine, with notifications (e.g., 0x023F) confirming actions. This meticulous process, decoding hexadecimal payloads, enabled Rahul to control the camera programmatically.
Building a KMP Remote Camera App
With the protocol cracked, Rahul built a KMP app using Compose Multiplatform, targeting Android and desktop. The app’s BLE scanner filters for Sony’s manufacturer data (0x03000), ignoring irrelevant metadata like model codes. Connection logic uses Kotlin Flows to monitor peripheral state, ensuring seamless reconnections. Capturing a photo involves sending reset and focus commands to FF01, awaiting focus confirmation on FF02, then triggering capture and shutter reset. For advanced features, Rahul integrated Sony’s C SDK via cinterop, navigating its complexities to access functions like interval shooting. His live demo, despite an initially powered-off camera, succeeded when the camera advertised, and a button click took a photo, earning audience cheers. The app’s simplicity contrasts Sony’s feature-heavy apps, proving KMP’s power for cross-platform development. Rahul’s GitHub repository offers the code, inviting developers to explore BLE and KMP for their own projects.
Links:
Hashtags: #KotlinMultiplatform #BluetoothLE
[KotlinConf2023] Java and Kotlin: A Mutual Evolution
At KotlinConf2024, John Pampuch, Google’s production languages lead, delivered a history lesson on Java and Kotlin’s intertwined journeys. Battling jet lag with humor, John traced nearly three decades of Java and twelve years of Kotlin, emphasizing their complementary strengths. From Java’s robust ecosystem to Kotlin’s pragmatic innovation, the languages have shaped each other, accelerating progress. John’s talk, rooted in his experience since Java’s 1996 debut, explored design goals, feature cross-pollination, and future implications, urging developers to leverage Kotlin’s developer-friendly features while appreciating Java’s stability.
Design Philosophies: Pragmatism Meets Robustness
John opened by contrasting the languages’ origins. Java, launched in 1995, aimed for simplicity, security, and portability, aligning tightly with the JVM and JDK. Its ecosystem, bolstered by libraries and tooling, set a standard for enterprise development. Kotlin, announced in 2011 by JetBrains, prioritized pragmatism: concise syntax, interoperability with Java, and multiplatform flexibility. Unlike Java’s JVM dependency, Kotlin targets iOS, web, and beyond, enabling faster feature rollouts. John noted Kotlin’s design avoids Java’s rigidity, embracing object-oriented principles with practical tweaks like semicolon-free lines. Yet Java’s self-consistency, seen in its holistic lambda integration, complements Kotlin’s adaptability, creating a synergy where both thrive.
Feature Evolution: From Lambdas to Coroutines
The talk highlighted key milestones. Java’s 2014 release of JDK 8 introduced lambdas, default methods, and type inference, transforming APIs to support functional programming. Kotlin, with 1.0 in 2016, brought smart casts, string templates, and named arguments, prioritizing developer ease. By 2018, Kotlin’s coroutines revolutionized JVM asynchronous programming, offering a simpler mental model than Java’s threads. John praised coroutines as a potential game-changer, though Java’s 2023 virtual threads and structured concurrency aim to close the gap. Kotlin’s multiplatform support, cemented by Google’s 2017 Android endorsement, outpaces Java’s JVM-centric approach, but Java’s predictable six-month release cycle since 2017 ensures steady progress. These advancements reflect a race where each language pushes the other forward.
Mutual Influences: Sealed Classes and Beyond
John emphasized cross-pollination. Java’s 2021 records, inspired by frameworks like Lombok, mirror Kotlin’s data classes, though Kotlin’s named parameters reduce boilerplate further. Sealed classes, introduced in Java 17 and Kotlin 1.5 around 2021, emerged concurrently, suggesting shared inspiration. Kotlin’s string templates, a staple since its early days, influenced Java’s 2024 preview of flexible string templates, which John hopes Kotlin might adopt for localization. Java’s exploration of nullability annotations, potentially aligning with Kotlin’s robust null safety, shows ongoing convergence. John speculated that community demand could push Java toward features like named arguments, though JVM changes remain a hurdle. This mutual learning, fueled by competition with languages like Go and Rust, drives excitement and innovation.
Looking Ahead: Pragmatism and Compatibility
John concluded with a call to action: embrace Kotlin’s compact, readable features while valuing Java’s compile-time speed and ecosystem. Kotlin’s faster feature delivery and multiplatform prowess contrast with Java’s backwards compatibility and predictability. Yet both share a commitment to pragmatic evolution, avoiding breaks in millions of applications. Questions from the audience probed Java’s nullability and virtual threads, with John optimistic about eventual alignment but cautious about timelines. His talk underscored that Java and Kotlin’s competition isn’t zero-sum—it’s a catalyst for better tools, ideas, and developer experiences, ensuring both languages remain vital.
Links:
Hashtags: #Java #Kotlin
[KotlinConf’2023] Coroutines and Loom: A Deep Dive into Goals and Implementations
The advent of OpenJDK’s Project Loom and its virtual threads has sparked considerable discussion within the Java and Kotlin communities, particularly regarding its relationship with Kotlin Coroutines. Roman Elizarov, Project Lead for Kotlin at JetBrains, addressed this topic head-on at KotlinConf’23 in his talk, “Coroutines and Loom behind the scenes”. His goal was not just to answer whether Loom would make coroutines obsolete (the answer being a clear “no”), but to delve into the distinct design goals, implementations, and trade-offs of each, clarifying how they can coexist and even complement each other. Information about Project Loom can often be found via OpenJDK resources or articles like those on Baeldung.
Roman began by noting that Project Loom, introducing virtual threads to the JVM, was nearing stability, targeted for Java 21 (late 2023). He emphasized that understanding the goals behind each technology is crucial, as these goals heavily influence their design and optimal use cases.
Project Loom: Simplifying Server-Side Concurrency
Project Loom’s primary design goal, as Roman Elizarov explained, is to preserve the thread-per-request programming style prevalent in many existing Java server-side applications, while dramatically increasing scalability. Traditionally, assigning one platform thread per incoming request becomes a bottleneck due to the high cost of platform threads. Virtual threads aim to solve this by providing lightweight, JVM-managed threads that can run existing synchronous, blocking Java code with minimal or no changes. This allows legacy applications to scale much better without requiring a rewrite to asynchronous or reactive patterns.
Loom achieves this by “unmounting” a virtual thread from its carrier (platform) thread when it encounters a blocking operation (like I/O) that has been integrated with Loom. The carrier thread is then free to run other virtual threads. When the blocking operation completes, the virtual thread is “remounted” on a carrier thread to continue execution. This mechanism is largely transparent to the application code. However, Roman pointed out a potential pitfall: if blocking operations occur within synchronized
blocks or native JNI calls that haven’t been adapted for Loom, the carrier thread can get “pinned,” preventing unmounting and potentially negating some of Loom’s benefits in those specific scenarios.
Kotlin Coroutines: Fine-Grained, Structured Concurrency
In contrast, Kotlin Coroutines were designed with different primary goals:
- Enable fine-grained concurrency: Allowing developers to easily launch tens of thousands or even millions of concurrent tasks without performance issues, suitable for highly concurrent applications like UI event handling or complex data processing pipelines.
- Provide structured concurrency: Ensuring that the lifecycle of coroutines is managed within scopes, simplifying cancellation and preventing resource leaks. This is particularly critical for UI applications where tasks need to be cancelled when UI components are destroyed.
Kotlin Coroutines achieve this through suspendable functions (suspend fun
) and a compiler-based transformation. When a coroutine suspends, it doesn’t block its underlying thread; instead, its state is saved, and the thread is released to do other work. This is fundamentally different from Loom’s approach, which aims to make blocking calls non-problematic for virtual threads. Coroutines explicitly distinguish between suspending and non-suspending code, a design choice that enables features like structured concurrency but requires a different programming model than traditional blocking code.
Comparing Trade-offs and Performance
Roman Elizarov presented a detailed comparison:
- Programming Model: Loom aims for compatibility with existing blocking code. Coroutines introduce a new model with suspend functions, which is more verbose for simple blocking calls but enables powerful features like structured concurrency and explicit cancellation. Forcing blocking calls into a coroutine world requires wrappers like withContext(Dispatchers.IO), while Loom handles blocking calls transparently on virtual threads.
- Cost of Operations:
- Launching: Launching a coroutine is significantly cheaper than starting even a virtual thread, as coroutines are lighter weight objects.
- Yielding/Suspending: Suspending a coroutine is generally cheaper than a virtual thread yielding (unmounting/remounting), due to compiler optimizations in Kotlin for state machine management. Roman showed benchmarks indicating lower memory allocation and faster execution for coroutine suspension compared to virtual thread context switching in preview builds of Loom.
- Error Handling & Cancellation: Coroutines have built-in, robust support for structured cancellation. Loom’s virtual threads rely on Java’s traditional thread interruption mechanisms, which are less integrated into the programming model for cooperative cancellation.
- Debugging: Loom’s virtual threads offer a debugging experience very similar to traditional threads, with understandable stack traces. Coroutines, due to their state-machine nature, can sometimes have more complex stack traces, though IDE support has improved this.
Coexistence and Future Synergies
Roman Elizarov concluded that Loom and coroutines are designed for different primary use cases and will coexist effectively.
- Loom excels for existing Java applications using the thread-per-request model that need to scale without major rewrites.
- Coroutines excel for applications requiring fine-grained, highly concurrent operations, structured concurrency, and explicit cancellation management, often seen in UI applications or complex backend services with many interacting components.
He also highlighted a potential future synergy: Kotlin Coroutines could leverage Loom’s virtual threads for their Dispatchers.IO
(or a similar dispatcher) when running on newer JVMs. This could allow blocking calls within coroutines (those wrapped in withContext(Dispatchers.IO)
) to benefit from Loom’s efficient handling of blocking operations, potentially eliminating the need for a large, separate thread pool for I/O-bound tasks in coroutines. This would combine the benefits of both: coroutines for structured, fine-grained concurrency and Loom for efficient handling of any unavoidable blocking calls.
Links:
Hashtags: #Kotlin #Coroutines #ProjectLoom #Java #JVM #Concurrency #AsynchronousProgramming #RomanElizarov #JetBrains