Upgrade from Square Java SDK v40.1.0 to v44.2.0 - Questions About Optional<T> Structure

Hi Square Team,

I am preparing to upgrade the Square Java SDK from version 40.1.0.20240604 to 44.2.0.20250521.

However, I’ve noticed that all the get methods now return Optional<T>, requiring me to unwrap the Optional<T> each time to access the actual value. This significantly increases the verbosity of the code and is quite painful to work with during integration.

Could you please advise:

  1. Is there a more convenient or recommended way to use the SDK to avoid repetitive Optional unwrapping in our codebase?
  2. If possible, could you please share the reasoning behind the decision to change all properties to return Optional<T> in the new SDK version?

Understanding the design intention will help us better align our integration practices with the SDK’s direction.

Thank you very much for your support!

Best regards,
Murphy

:waving_hand: I’m Square Dev AI, here to help answer your question!
Based on the information you’ve provided, the following documentation will help answer your question:

Additional Documentation

Common Square API Patterns
Square Java SDK Quickstart
Versioning in the Square API

If this documentation helped you find a solution, please check the box indicating that this reply solves the problem.
Otherwise one of our Developer Advocates will provide additional assistance shortly.

Hey @murphyzhang! We’d recommend using the map function instead of an isPresent and get check to simplify this:

foo.map(value => {})

As far as reasoning goes, returning Optional lowers the likelihood of runtime exceptions due to null values and enforces null safety.

Thank you for explaining how to work with Optional<T> in the new SDK.

However, I’m still having difficulty understanding one design aspect:
With CatalogObject, I can no longer directly get the id but instead need to retrieve a nested child object to get the id.

public static String getId(CatalogObject c) {
    if (c.isItem()) return c.getItem().get().getId();
    if (c.isItemVariation()) return c.getItemVariation().get().getId();
    if (c.isTax()) return c.getTax().get().getId();
    if (c.isCategory()) return c.getCategory().flatMap(CatalogObjectCategory::getId).get();
    if (c.isModifierList()) return c.getModifierList().get().getId();
    if (c.isModifier()) return c.getModifier().get().getId();
    if (c.isImage()) return c.getImage().get().getId();
    return null;
}

I have an additional question regarding the new SDK design:

I don’t quite understand why the id of a CatalogObject is designed to be nullable. Conceptually, the id seems like it should always exist and be non-null for a CatalogObject.

Could you please help me understand the reasoning behind this design? It feels unintuitive compared to the previous version, and I would like to understand the benefit of this approach to better align our integration practices.

Thank you for your clarification!

Best regards,
Murphy

Hi Square Team:

If you have any updates, please feel free to let me know at any time.

Thanks,

Murphy

The CatalogObject is a polymorphic container that can represent different types of catalog entities (items, variations, categories, etc.). Each specific type has its own properties and constraints, including how IDs are managed.

For example:

  • Items have their own ID structure
  • Item Variations have IDs that relate to their parent items
  • Categories may have optional IDs in certain contexts

Better Pattern for ID Access

Instead of the complex conditional logic you showed, here’s a more elegant approach using the new SDK:

public static Optional<String> getId(CatalogObject catalogObject) {
    // Use the top-level ID if available
    return catalogObject.getId();
}

// Or if you need type-specific behavior:
public static Optional<String> getTypeSpecificId(CatalogObject catalogObject) {
    return catalogObject.getId()
        .or(() -> catalogObject.getItem().flatMap(CatalogItem::getId))
        .or(() -> catalogObject.getItemVariation().flatMap(CatalogItemVariation::getId))
        .or(() -> catalogObject.getCategory().flatMap(CatalogCategory::getId));
}

Why Nullable IDs Make Sense

The nullable design reflects real-world API scenarios:

  1. Draft Objects: When creating new catalog objects, they may not have IDs yet
  2. Partial Updates: Some API responses may include partial object data
  3. Different Creation Contexts: Certain catalog objects are created with server-generated IDs
  4. API Consistency: This aligns with how the REST API actually behaves :slight_smile:

Thank you very much for your support. Your explanation is exceptionally clear and greatly appreciated.