包含信用卡过期年份的字符串数组,从当年算起再算 10 年

String array containing expiration credit card years from current year count 10 more years

对于 credit/debit 卡片的到期日期,我使用的数组包含从 2020 年到 2030 年的所有年份:

String[] expirationYearArray = { "2020", "2021", "2022", "2023", "2024", "2025", "2026", "2027", "2028", "2029", "2030" };

这是一种糟糕的方法,因为年份是硬编码的。也就是说,3年后,也就是2023年,用户仍然可以选择2020年、2021年和2022年作为信用卡到期日。这显然是错误的。我想要做的是用当前年份的字符串填充数组到当前年份的 10 年以上。我想做的是使用 Java 内置函数来获取当前年份。然后使用 for 循环进行 10 次迭代,并在每次迭代中从 Int 转换为 String,以便我将 2020 转换为 "2020",然后将 "2020" 作为 [= 的第一个元素14=]数组。 for 或 while 循环将继续这样做,直到我到达第十次迭代。这种方法对你有意义吗?如果您看到一个不同的或更优化的选项,可以用更少的代码做同样的事情,或者它是否像我设想的那样对您有意义,请告诉我。谢谢。

这里有几个变体:

  public static void main(String[] args) {

    System.out.println(Arrays.toString(getExpirationYears(LocalDateTime.now().getYear())));
    System.out.println(Arrays.toString(getExpirationYears(Calendar.getInstance().get(Calendar.YEAR))));
    System.out.println(Arrays.toString(getExpirationYears(1900 + new Date().getYear())));
    System.out.println(Arrays.toString(getExpirationYearByStreams(1900 + new Date().getYear())));
}

static String[] getExpirationYears(int year) {
    String[] expirationYearArray = new String[10];
    for (int i = 0; i < expirationYearArray.length; i++) {
        expirationYearArray[i] = String.valueOf(year + i);
    }
    return expirationYearArray;
}


static String[] getExpirationYearByStreams(int year) {
   return IntStream.range(year, year+10)
            .boxed()
            .map(String::valueOf)
            .toArray(String[]::new);

}

tl;博士

2 行,使用流和 lambda 语法。

Year currentYear = Year.now( ZoneId.of( "Pacific/Auckland" ) );  // Determining the current year requires a time zone when near the ending/beginning of the year.
List < Year > years =                                            // A list of objects of the class `Year`. Better to use a specific type than use a mere integer number.
        IntStream                                                // Generates a stream of `int` primitive numbers.
        .range(                                                  // Get numbers from the specified beginning to the specified ending.
            currentYear.getValue() ,                             // Get `int` number of the current year.
            currentYear.plusYears( 10 ).getValue()               // Yet the `int` number of a later year.
        )                                                        // Returns a `IntStream` object.
        .boxed()                                                 // Converts the `int` primitive values into a stream of `Integer` objects.
        .map( integer -> Year.of( integer ) )                    // Uses each `Integer` object to instantiate a `Year` object. The `Integer` object is auto-boxed back into an `int` primitive`. Primitives cannot be mapped in a stream with a lambda. So we had to do this funky `int` -> `Integer` -> `int` conversion juggling.
        .collect( Collectors.toList() )                          // Gather the `Year` objects produced by the `map` method into a `List` collection.
;

几行,使用常规语法。

Year currentYear = Year.now( ZoneId.of( "Asia/Tokyo" ) ) ;
List< Year > years = new ArrayList<>( 10 ) ;
for( int i = 0 ; i < 10 ; i ++ ) 
{
    years.add( currentYear.plusYears( i ) ) ;
}

看到这个code run live at IdeOne.com

years.toString(): [2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029]

java.time.Year

Java 提供了一个特定的 class 来表示年份,恰当地命名为 Year。我建议使用 class 而不是单纯的整数来使您的代码类型安全并且更加自我记录。

时区至关重要

获取当前年份需要时区。对于任何给定时刻,世界各地的日期因时区而异。在日本东京 "tomorrow",同时在魁北克蒙特利尔 "yesterday"。

因此,在一年的最后一天/第一天前后,日本东京可能是 "next year",同时魁北克蒙特利尔可能是 "last year"。所以在确定当前年份时,指定desired/expected时区。

ZoneId z = ZoneId.of( "America/Montreal" ) ;
Year currentYear = Year.now( z ) ;

通过迭代获得你的下一个十年。

List< Year > years = new ArrayList<>( 10 ) ;
Year year = currentYear ;
for( int i = 0 ; i < 10 ; i ++ ) 
{
    // Remember this year.
    years.add( year ) ;
    // Set up the next loop.
    year = currentYear.plusYears( i ) ;
}

做一个unmodifiable list from that ArrayList by calling List.copyOf.

List< Year > years = List.copyOf( years ) ;  // Returns an unmodifiable `List`, with adding/removing elements disallowed.

java.time.Month

对于到期年月,您还需要一个用于选择月份的小部件。在那里你可以使用Monthclass。

List< Month > months = List.copyOf( Month.values() ) 

MonthtoString 方法根据枚举元素的名称为您提供全大写的英文月份名称。您可能想要制作一个本地化名称列表以显示给用户。

List< String > monthsLocalized = new ArrayList<>( months.size() ) ;
Locale locale = Locale.CANADA_FRENCH ;
for( Month month : months ) 
{
    monthsLocalized.add( month.getDisplayName( TextStyle.FULL , locale ) ;
}
monthsLocalized = List.copyOf( monthsLocalized ) ;

java.time.YearMonth

对于用户选择年份和月份的最终结果,请使用 YearMonth. Oddly, the YearMonth factory methods do not take a Year object, only a mere integer. So call Year::getValue 获取数字。

YearMonth yearMonth = YearMonth.of( selectedYear.getValue() , selectedMonth ) ;

过早的优化

您表达了对性能优化的担忧。这不是您需要优化的代码。确实,不要陷入过早优化的陷阱。事实证明,即使是最有经验的程序员也不善于凭直觉猜测代码库中的瓶颈所在。只有在经验证明显着降低会严重影响代码性能和业务 运行 之后才进行优化。大多数情况下,在 Java 编译器和运行时优化器(HotSpot、OpenJ9)完成工作后,编写简单直接的短行 Java 代码将导致高度优化的执行代码。

关于java.time

java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

要了解更多信息,请参阅 Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310

Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

您可以直接与数据库交换 java.time 对象。使用 JDBC driver compliant with JDBC 4.2 或更高版本。不需要字符串,不需要 java.sql.* classes.

从哪里获得java.time classes?

最好避免对年份列表进行硬编码。

至于您生成的年份范围,您可以向您的卡片处理商查询规则。例如,过期的卡 可以 在某些情况下被设置为定期收费(如订阅),但它们可能不应该被新计划接受。同样,信用卡通常会在 10 年内到期;您的处理器可能会拒绝过期时间过长的卡片。

同样,处理器在执行时区方面可能不一致。有些人可能会考虑在当地时区结束时结束一年,其他人可能会使用 UTC。最宽松的政策是使用 "anywhere on earth" zone, 与 UTC 的偏移量 -12:00。

要使您的逻辑可测试,请将其传递给 Clock 实例。通常,时钟将是系统默认时钟,但在这种情况下,您可以将区域设置为 "anywhere in the world"。通过传递一个时钟,您可以去除对实际时间的依赖,并随时测试您的计算将如何表现。

private static final ZoneOffset aoe = ZoneOffset.ofHours(-12);

private static final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("uuuu");

public static void main(String[] args) {
    Clock clock = Clock.system(aoe);
    List<Year> years = nextYears(clock, 10L);
    years.stream().map(fmt::format).forEach(System.out::println);
}

private List<Year> nextYears(Clock clock, long count) {
    return Stream.iterate(Year.now(clock), year -> year.plusYears(1L))
        .limit(count)
        .collect(Collectors.toList());
}

这就是我按照 Nonika 的建议(请参阅他的回答)在我的代码中实现它的方法:

package com.myapp;
..........
public class Payment extends BaseActivity implements ResponseListener {
    ..........
    int currentYear = Calendar.getInstance().get(Calendar.YEAR);
    String[] cardYearArray = new String[10];
    ..........
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ..........
        cardYearArray=getExpirationYears(currentYear);
        SpinnerAdapter creditCardYearAdapter = new SpinnerAdapter(Payment.this,
            cardYearArray);
        ..........
    }
    static String[] getExpirationYears(int year) {
        String[] expirationYearArray = new String[10];
        for (int i = 0; i < expirationYearArray.length; i++) {
            expirationYearArray[i] = String.valueOf(year + i);
        }
        return expirationYearArray;
    }
    ..........
}

现在我看到了我需要的年份正确显示: